719 lines
16 KiB
Markdown
719 lines
16 KiB
Markdown
# Health Check API Integration
|
|
|
|
This document describes the integration between the MaplePress frontend and the Health Check API endpoint.
|
|
|
|
## Table of Contents
|
|
|
|
- [Overview](#overview)
|
|
- [Backend API Specification](#backend-api-specification)
|
|
- [Frontend Implementation](#frontend-implementation)
|
|
- [Service Methods](#service-methods)
|
|
- [Usage Examples](#usage-examples)
|
|
- [Use Cases](#use-cases)
|
|
- [Error Handling](#error-handling)
|
|
- [Testing](#testing)
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The Health Check API is a simple, unauthenticated endpoint that verifies the MaplePress backend service is running and operational. It's commonly used for:
|
|
|
|
- **Monitoring**: Service availability checks
|
|
- **Load Balancers**: Health probe endpoints
|
|
- **Startup Verification**: Ensuring backend is ready before initializing frontend
|
|
- **API Connectivity**: Testing network connectivity to backend
|
|
|
|
### Key Features
|
|
|
|
- ✅ **No Authentication Required**: Public endpoint
|
|
- ✅ **Simple Response**: Returns `{ "status": "healthy" }`
|
|
- ✅ **Fast Response**: Lightweight check with minimal processing
|
|
- ✅ **Always Available**: Does not depend on database or external services
|
|
|
|
---
|
|
|
|
## Backend API Specification
|
|
|
|
### Endpoint
|
|
|
|
```
|
|
GET /health
|
|
```
|
|
|
|
### Authentication
|
|
|
|
**None required** - This is a public endpoint.
|
|
|
|
### Request Headers
|
|
|
|
No headers required.
|
|
|
|
### Request Parameters
|
|
|
|
None.
|
|
|
|
### Response (200 OK)
|
|
|
|
```json
|
|
{
|
|
"status": "healthy"
|
|
}
|
|
```
|
|
|
|
### Response Fields
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| status | string | Health status - always "healthy" when service is running |
|
|
|
|
### Error Responses
|
|
|
|
- `503 Service Unavailable`: Backend service is down or unreachable
|
|
- Network errors: Connection refused, timeout, etc.
|
|
|
|
### Backend Implementation
|
|
|
|
**Handler File**: `cloud/maplepress-backend/internal/interface/http/handler/healthcheck/healthcheck_handler.go`
|
|
|
|
```go
|
|
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) {
|
|
response := map[string]string{
|
|
"status": "healthy",
|
|
}
|
|
httpresponse.OK(w, response)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Frontend Implementation
|
|
|
|
### Service File
|
|
|
|
**Location**: `src/services/API/HealthService.js`
|
|
|
|
### Dependencies
|
|
|
|
```javascript
|
|
import ApiClient from "./ApiClient";
|
|
```
|
|
|
|
### Architecture
|
|
|
|
The HealthService follows the same three-layer architecture as other API services:
|
|
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ React Components (UI) │
|
|
│ - Dashboard, Status Indicators │
|
|
└─────────────┬───────────────────────┘
|
|
│
|
|
↓
|
|
┌─────────────────────────────────────┐
|
|
│ HealthService (API Layer) │
|
|
│ - checkHealth() │
|
|
│ - isHealthy() │
|
|
│ - waitUntilHealthy() │
|
|
│ - getDetailedStatus() │
|
|
└─────────────┬───────────────────────┘
|
|
│
|
|
↓
|
|
┌─────────────────────────────────────┐
|
|
│ ApiClient (HTTP Layer) │
|
|
│ - GET /health │
|
|
└─────────────┬───────────────────────┘
|
|
│
|
|
↓
|
|
┌─────────────────────────────────────┐
|
|
│ Backend API │
|
|
│ GET /health → { status: "healthy" }│
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Service Methods
|
|
|
|
### 1. `checkHealth()`
|
|
|
|
Check if the backend service is healthy.
|
|
|
|
**Signature**:
|
|
```javascript
|
|
async function checkHealth(): Promise<Object>
|
|
```
|
|
|
|
**Returns**:
|
|
```javascript
|
|
{
|
|
status: "healthy"
|
|
}
|
|
```
|
|
|
|
**Throws**: `Error` if service is unreachable or unhealthy
|
|
|
|
**Example**:
|
|
```javascript
|
|
import HealthService from './services/API/HealthService';
|
|
|
|
try {
|
|
const health = await HealthService.checkHealth();
|
|
if (health.status === 'healthy') {
|
|
console.log('✅ Backend is healthy');
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Backend is down:', error.message);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. `isHealthy()`
|
|
|
|
Simple boolean check for backend health.
|
|
|
|
**Signature**:
|
|
```javascript
|
|
async function isHealthy(): Promise<boolean>
|
|
```
|
|
|
|
**Returns**: `true` if backend is healthy, `false` otherwise
|
|
|
|
**Example**:
|
|
```javascript
|
|
const healthy = await HealthService.isHealthy();
|
|
|
|
if (healthy) {
|
|
console.log('✅ Backend is ready');
|
|
} else {
|
|
console.error('❌ Backend is not available');
|
|
}
|
|
```
|
|
|
|
**Use Case**: Quick status checks, conditional rendering
|
|
|
|
---
|
|
|
|
### 3. `waitUntilHealthy()`
|
|
|
|
Wait for the backend to become healthy (with retries).
|
|
|
|
**Signature**:
|
|
```javascript
|
|
async function waitUntilHealthy(options?: Object): Promise<boolean>
|
|
```
|
|
|
|
**Parameters**:
|
|
```javascript
|
|
{
|
|
maxAttempts: number, // Maximum retry attempts (default: 30)
|
|
retryDelayMs: number // Delay between attempts in ms (default: 1000)
|
|
}
|
|
```
|
|
|
|
**Returns**: `true` if backend became healthy, `false` if timeout
|
|
|
|
**Example**:
|
|
```javascript
|
|
// Wait up to 10 seconds (10 attempts x 1 second)
|
|
const ready = await HealthService.waitUntilHealthy({
|
|
maxAttempts: 10,
|
|
retryDelayMs: 1000
|
|
});
|
|
|
|
if (ready) {
|
|
console.log('✅ Backend is ready!');
|
|
// Proceed with app initialization
|
|
} else {
|
|
console.error('❌ Backend did not become ready in time');
|
|
// Show error message to user
|
|
}
|
|
```
|
|
|
|
**Use Case**: Application startup, waiting for backend deployment
|
|
|
|
---
|
|
|
|
### 4. `getDetailedStatus()`
|
|
|
|
Get detailed health status with timing information.
|
|
|
|
**Signature**:
|
|
```javascript
|
|
async function getDetailedStatus(): Promise<Object>
|
|
```
|
|
|
|
**Returns**:
|
|
```javascript
|
|
{
|
|
healthy: boolean, // Whether backend is healthy
|
|
status: string, // "healthy" or "unhealthy"
|
|
responseTimeMs: number, // Response time in milliseconds
|
|
timestamp: Date, // When check was performed
|
|
error?: string // Error message if unhealthy
|
|
}
|
|
```
|
|
|
|
**Example**:
|
|
```javascript
|
|
const status = await HealthService.getDetailedStatus();
|
|
|
|
console.log(`Backend Status: ${status.status}`);
|
|
console.log(`Response Time: ${status.responseTimeMs}ms`);
|
|
console.log(`Checked At: ${status.timestamp.toISOString()}`);
|
|
|
|
if (!status.healthy) {
|
|
console.error(`Error: ${status.error}`);
|
|
}
|
|
```
|
|
|
|
**Use Case**: Monitoring dashboards, performance tracking, diagnostics
|
|
|
|
---
|
|
|
|
## Usage Examples
|
|
|
|
### Example 1: Application Startup Check
|
|
|
|
Check backend health before initializing the app:
|
|
|
|
```javascript
|
|
// In App.jsx or main.jsx
|
|
import { useEffect, useState } from 'react';
|
|
import HealthService from './services/API/HealthService';
|
|
|
|
function App() {
|
|
const [backendReady, setBackendReady] = useState(false);
|
|
const [error, setError] = useState(null);
|
|
|
|
useEffect(() => {
|
|
async function checkBackend() {
|
|
try {
|
|
const healthy = await HealthService.isHealthy();
|
|
setBackendReady(healthy);
|
|
|
|
if (!healthy) {
|
|
setError('Backend service is not available');
|
|
}
|
|
} catch (err) {
|
|
setError(err.message);
|
|
}
|
|
}
|
|
|
|
checkBackend();
|
|
}, []);
|
|
|
|
if (error) {
|
|
return <div className="error">Backend Error: {error}</div>;
|
|
}
|
|
|
|
if (!backendReady) {
|
|
return <div className="loading">Connecting to backend...</div>;
|
|
}
|
|
|
|
return <div>App content...</div>;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Example 2: Status Indicator Component
|
|
|
|
Show real-time backend status:
|
|
|
|
```javascript
|
|
import { useEffect, useState } from 'react';
|
|
import HealthService from '../services/API/HealthService';
|
|
|
|
function BackendStatusIndicator() {
|
|
const [status, setStatus] = useState(null);
|
|
|
|
useEffect(() => {
|
|
// Check status every 30 seconds
|
|
const checkStatus = async () => {
|
|
const detail = await HealthService.getDetailedStatus();
|
|
setStatus(detail);
|
|
};
|
|
|
|
checkStatus();
|
|
const interval = setInterval(checkStatus, 30000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
if (!status) {
|
|
return <span>Checking...</span>;
|
|
}
|
|
|
|
return (
|
|
<div className={`status-indicator ${status.healthy ? 'healthy' : 'unhealthy'}`}>
|
|
<span className="status-dot"></span>
|
|
<span className="status-text">
|
|
Backend: {status.status} ({status.responseTimeMs}ms)
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Example 3: Deployment Health Check
|
|
|
|
Wait for backend after deployment:
|
|
|
|
```javascript
|
|
import HealthService from './services/API/HealthService';
|
|
|
|
async function waitForBackend() {
|
|
console.log('Waiting for backend to become ready...');
|
|
|
|
const ready = await HealthService.waitUntilHealthy({
|
|
maxAttempts: 60, // Wait up to 1 minute
|
|
retryDelayMs: 1000 // Check every second
|
|
});
|
|
|
|
if (ready) {
|
|
console.log('✅ Backend is ready!');
|
|
return true;
|
|
} else {
|
|
console.error('❌ Backend deployment timeout');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Usage in deployment script
|
|
if (await waitForBackend()) {
|
|
// Proceed with app initialization
|
|
} else {
|
|
// Show deployment error message
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Example 4: Error Recovery
|
|
|
|
Retry failed API calls after backend recovers:
|
|
|
|
```javascript
|
|
import HealthService from './services/API/HealthService';
|
|
import SiteService from './services/API/SiteService';
|
|
|
|
async function fetchSitesWithRetry() {
|
|
try {
|
|
// Try to fetch sites
|
|
const sites = await SiteService.listSites();
|
|
return sites;
|
|
} catch (error) {
|
|
console.error('Failed to fetch sites:', error);
|
|
|
|
// Check if backend is healthy
|
|
const healthy = await HealthService.isHealthy();
|
|
|
|
if (!healthy) {
|
|
console.log('Backend is down, waiting for recovery...');
|
|
|
|
// Wait for backend to recover
|
|
const recovered = await HealthService.waitUntilHealthy({
|
|
maxAttempts: 10,
|
|
retryDelayMs: 2000
|
|
});
|
|
|
|
if (recovered) {
|
|
console.log('Backend recovered, retrying...');
|
|
return await SiteService.listSites();
|
|
}
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Use Cases
|
|
|
|
### 1. **Monitoring & Observability**
|
|
|
|
- Service uptime monitoring
|
|
- Response time tracking
|
|
- Health dashboard indicators
|
|
- Alerting on service degradation
|
|
|
|
### 2. **Load Balancer Integration**
|
|
|
|
- Health probe endpoint for AWS/Azure/GCP load balancers
|
|
- Kubernetes liveness/readiness probes
|
|
- Docker healthcheck configuration
|
|
|
|
### 3. **Application Lifecycle**
|
|
|
|
- Startup health verification
|
|
- Graceful degradation on backend issues
|
|
- Post-deployment verification
|
|
- Environment validation (dev/staging/prod)
|
|
|
|
### 4. **User Experience**
|
|
|
|
- Show connection status to users
|
|
- Prevent API calls when backend is down
|
|
- Display maintenance mode messages
|
|
- Automatic retry on recovery
|
|
|
|
### 5. **Development & Testing**
|
|
|
|
- Verify backend is running before tests
|
|
- E2E test prerequisites
|
|
- Local development environment checks
|
|
- CI/CD pipeline health gates
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
### Network Errors
|
|
|
|
```javascript
|
|
try {
|
|
await HealthService.checkHealth();
|
|
} catch (error) {
|
|
if (error.message.includes('network') || error.message.includes('fetch')) {
|
|
// Network connectivity issue
|
|
console.error('Cannot reach backend - check network');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Service Unavailable
|
|
|
|
```javascript
|
|
try {
|
|
await HealthService.checkHealth();
|
|
} catch (error) {
|
|
if (error.message.includes('503') || error.message.includes('unavailable')) {
|
|
// Backend is down or restarting
|
|
console.error('Backend service is unavailable');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Timeout Handling
|
|
|
|
```javascript
|
|
// With timeout wrapper
|
|
async function checkHealthWithTimeout(timeoutMs = 5000) {
|
|
const timeoutPromise = new Promise((_, reject) =>
|
|
setTimeout(() => reject(new Error('Health check timeout')), timeoutMs)
|
|
);
|
|
|
|
try {
|
|
return await Promise.race([
|
|
HealthService.checkHealth(),
|
|
timeoutPromise
|
|
]);
|
|
} catch (error) {
|
|
console.error('Health check failed or timed out:', error.message);
|
|
return { status: 'unhealthy' };
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Manual Testing
|
|
|
|
1. **Start the backend**:
|
|
```bash
|
|
cd cloud/maplepress-backend
|
|
task dev
|
|
```
|
|
|
|
2. **Test with curl**:
|
|
```bash
|
|
curl -X GET http://localhost:8000/health
|
|
```
|
|
|
|
Expected response:
|
|
```json
|
|
{ "status": "healthy" }
|
|
```
|
|
|
|
3. **Test from frontend**:
|
|
```javascript
|
|
// In browser console
|
|
import HealthService from './services/API/HealthService.js';
|
|
|
|
const health = await HealthService.checkHealth();
|
|
console.log(health); // { status: "healthy" }
|
|
```
|
|
|
|
### Automated Testing
|
|
|
|
```javascript
|
|
// Example test (using Jest or Vitest)
|
|
import HealthService from '../services/API/HealthService';
|
|
|
|
describe('HealthService', () => {
|
|
it('should return healthy status when backend is up', async () => {
|
|
const health = await HealthService.checkHealth();
|
|
expect(health.status).toBe('healthy');
|
|
});
|
|
|
|
it('should return true for isHealthy()', async () => {
|
|
const healthy = await HealthService.isHealthy();
|
|
expect(healthy).toBe(true);
|
|
});
|
|
|
|
it('should include response time in detailed status', async () => {
|
|
const status = await HealthService.getDetailedStatus();
|
|
expect(status).toHaveProperty('responseTimeMs');
|
|
expect(status.responseTimeMs).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: "Unable to connect to backend service"
|
|
|
|
**Symptoms**: Network or fetch errors
|
|
|
|
**Solutions**:
|
|
1. Verify backend is running: `curl http://localhost:8000/health`
|
|
2. Check `VITE_API_BASE_URL` in `.env` file
|
|
3. Verify no CORS issues in browser console
|
|
4. Check firewall/network settings
|
|
|
|
### Issue: "Backend service is temporarily unavailable"
|
|
|
|
**Symptoms**: 503 status code
|
|
|
|
**Solutions**:
|
|
1. Backend may be starting up - wait a few seconds
|
|
2. Check backend logs: `docker logs mapleopentech_backend`
|
|
3. Verify backend services (Cassandra, Redis) are running
|
|
4. Restart backend: `task end && task dev`
|
|
|
|
### Issue: Health check timeout
|
|
|
|
**Symptoms**: Slow or no response
|
|
|
|
**Solutions**:
|
|
1. Check backend server load
|
|
2. Verify network latency
|
|
3. Check if backend is overloaded
|
|
4. Consider increasing timeout in `waitUntilHealthy()`
|
|
|
|
### Issue: Always returns unhealthy
|
|
|
|
**Symptoms**: `isHealthy()` always returns false
|
|
|
|
**Solutions**:
|
|
1. Check browser console for errors
|
|
2. Verify API base URL is correct
|
|
3. Check CORS configuration
|
|
4. Test endpoint directly with curl
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### 1. **Use During Initialization**
|
|
|
|
Always check backend health during app startup:
|
|
|
|
```javascript
|
|
useEffect(() => {
|
|
HealthService.isHealthy().then(healthy => {
|
|
if (!healthy) {
|
|
showBackendError();
|
|
}
|
|
});
|
|
}, []);
|
|
```
|
|
|
|
### 2. **Periodic Health Checks**
|
|
|
|
For long-running apps, check periodically:
|
|
|
|
```javascript
|
|
// Every 5 minutes
|
|
setInterval(async () => {
|
|
const healthy = await HealthService.isHealthy();
|
|
updateStatusIndicator(healthy);
|
|
}, 300000);
|
|
```
|
|
|
|
### 3. **Handle Failures Gracefully**
|
|
|
|
Don't throw errors to users - handle them gracefully:
|
|
|
|
```javascript
|
|
const healthy = await HealthService.isHealthy().catch(() => false);
|
|
if (!healthy) {
|
|
showOfflineMode();
|
|
}
|
|
```
|
|
|
|
### 4. **Log Response Times**
|
|
|
|
Monitor performance over time:
|
|
|
|
```javascript
|
|
const status = await HealthService.getDetailedStatus();
|
|
analytics.track('backend_health', {
|
|
responseTime: status.responseTimeMs,
|
|
healthy: status.healthy
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Integration with Other Services
|
|
|
|
The HealthService can be combined with other services for robust error handling:
|
|
|
|
```javascript
|
|
import HealthService from './services/API/HealthService';
|
|
import AuthManager from './services/Manager/AuthManager';
|
|
|
|
async function safeLogin(email, password) {
|
|
// Check backend health first
|
|
const healthy = await HealthService.isHealthy();
|
|
|
|
if (!healthy) {
|
|
throw new Error('Backend is currently unavailable. Please try again later.');
|
|
}
|
|
|
|
// Proceed with login
|
|
return await AuthManager.login({ email, password });
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
The Health Check API provides a simple, reliable way to verify backend availability:
|
|
|
|
- ✅ **Simple Integration**: One GET request, no auth required
|
|
- ✅ **Multiple Helper Methods**: `checkHealth()`, `isHealthy()`, `waitUntilHealthy()`, `getDetailedStatus()`
|
|
- ✅ **Error Handling**: Comprehensive error detection and user-friendly messages
|
|
- ✅ **Flexible Usage**: Startup checks, monitoring, status indicators, deployment verification
|
|
- ✅ **Production Ready**: Tested against backend implementation
|
|
|
|
For questions or issues, refer to the [Troubleshooting](#troubleshooting) section or check the main [README](../../README.md).
|
|
|
|
---
|
|
|
|
**Last Updated**: October 30, 2024
|
|
**Frontend Version**: 0.0.0
|
|
**Backend API Version**: 1.0.0
|