Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
719
web/maplepress-frontend/docs/API/HEALTH_API.md
Normal file
719
web/maplepress-frontend/docs/API/HEALTH_API.md
Normal file
|
|
@ -0,0 +1,719 @@
|
|||
# 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue