Health API - Dart SDK Documentation
Overview
The Health API provides a simple endpoint to check the health status of the server. It returns basic health information and, when authenticated as a superuser, provides additional diagnostic information about the server state.
Key Features:
- No authentication required for basic health check
- Superuser authentication provides additional diagnostic data
- Lightweight endpoint for monitoring and health checks
- Supports both GET and HEAD methods
Backend Endpoints:
GET /api/health- Check health statusHEAD /api/health- Check health status (HEAD method)
Note: The health endpoint is publicly accessible, but superuser authentication provides additional information.
Authentication
Basic health checks do not require authentication:
```dart import ‘package:bosbase/bosbase.dart’;
final pb = Bosbase(‘http://127.0.0.1:8090’);
// Basic health check (no auth required) final health = await pb.health.check(); ```
For additional diagnostic information, authenticate as a superuser:
```dart // Authenticate as superuser for extended health data await pb.collection(’_superusers’).authWithPassword(‘admin@example.com’, ‘password’); final health = await pb.health.check(); ```
Health Check Response Structure
Basic Response (Guest/Regular User)
```dart HealthCheck( code: 200, message: “API is healthy.”, data: {}, ) ```
Superuser Response
```dart HealthCheck( code: 200, message: “API is healthy.”, data: { ‘canBackup’: bool, // Whether backup operations are allowed ‘realIP’: String, // Real IP address of the client ‘requireS3’: bool, // Whether S3 storage is required ‘possibleProxyHeader’: String // Detected proxy header (if behind reverse proxy) }, ) ```
Check Health Status
Returns the health status of the API server.
Basic Usage
```dart // Simple health check final health = await pb.health.check();
print(health.message); // “API is healthy.” print(health.code); // 200 ```
With Superuser Authentication
```dart // Authenticate as superuser first await pb.collection(’_superusers’).authWithPassword(‘admin@example.com’, ‘password’);
// Get extended health information final health = await pb.health.check();
print(health.data[‘canBackup’]); // true/false print(health.data[‘realIP’]); // “192.168.1.100” print(health.data[‘requireS3’]); // false print(health.data[‘possibleProxyHeader’]); // "" or header name ```
Response Fields
Common Fields (All Users)
| Field | Type | Description |
|---|---|---|
| `code` | int | HTTP status code (always 200 for healthy server) |
| `message` | String | Health status message (“API is healthy.”) |
| `data` | Map<String, dynamic> | Health data (empty for non-superusers, populated for superusers) |
Superuser-Only Fields (in `data`)
| Field | Type | Description |
|---|---|---|
| `canBackup` | bool | `true` if backup/restore operations can be performed, `false` if a backup/restore is currently in progress |
| `realIP` | String | The real IP address of the client (useful when behind proxies) |
| `requireS3` | bool | `true` if S3 storage is required (local fallback disabled), `false` otherwise |
| `possibleProxyHeader` | String | Detected proxy header name (e.g., “X-Forwarded-For”, “CF-Connecting-IP”) if the server appears to be behind a reverse proxy, empty string otherwise |
Use Cases
1. Basic Health Monitoring
```dart
Future
if (health.code == 200 && health.message == "API is healthy.") {
print('✓ Server is healthy');
return true;
} else {
print('✗ Server health check failed');
return false;
}
} catch (e) { print(’✗ Health check error: $e’); return false; } }
// Use in monitoring Timer.periodic(Duration(minutes: 1), (_) async { final isHealthy = await checkServerHealth(); if (!isHealthy) { // Alert or take action print(‘Server health check failed!’); } }); ```
2. Backup Readiness Check
```dart
Future
final health = await pb.health.check();
final canBackup = health.data['canBackup'] as bool? ?? false;
if (!canBackup) {
print('⚠️ Backup operation is currently in progress');
return false;
}
print('✓ Backup operations are allowed');
return true;
} catch (e) { print(‘Failed to check backup readiness: $e’); return false; } }
// Use before creating backups if (await canPerformBackup()) { await pb.backups.create(‘backup.zip’); } ```
3. Monitoring Dashboard
```dart class HealthMonitor { final Bosbase pb; bool isSuperuser = false;
HealthMonitor(this.pb);
Future
Future<Map<String, dynamic» getHealthStatus() async { try { final health = await pb.health.check();
final status = <String, dynamic>{
'healthy': health.code == 200,
'message': health.message,
'timestamp': DateTime.now().toIso8601String(),
};
if (isSuperuser && health.data.isNotEmpty) {
status['diagnostics'] = {
'canBackup': health.data['canBackup'],
'realIP': health.data['realIP'],
'requireS3': health.data['requireS3'],
'behindProxy': health.data['possibleProxyHeader'] != null &&
(health.data['possibleProxyHeader'] as String).isNotEmpty,
'proxyHeader': health.data['possibleProxyHeader'] ?? '',
};
}
return status;
} catch (e) {
return {
'healthy': false,
'error': e.toString(),
'timestamp': DateTime.now().toIso8601String(),
};
}
} }
// Usage final monitor = HealthMonitor(pb); await monitor.authenticateAsSuperuser(‘admin@example.com’, ‘password’); final status = await monitor.getHealthStatus(); print(‘Health Status: $status’); ```
4. Load Balancer Health Check
```dart import ‘package:http/http.dart’ as http;
// Simple health check for load balancers
Future
// Use in server route for load balancer
Future
5. Proxy Detection
```dart Future<Map<String, dynamic» checkProxySetup() async { await pb.collection(’_superusers’).authWithPassword(‘admin@example.com’, ‘password’);
final health = await pb.health.check(); final proxyHeader = health.data[‘possibleProxyHeader’] as String? ?? ‘’;
if (proxyHeader.isNotEmpty) { print(’⚠️ Server appears to be behind a reverse proxy’); print(’ Detected proxy header: $proxyHeader’); print(’ Real IP: ${health.data[‘realIP’]}’);
// Provide guidance on trusted proxy configuration
print(' Ensure TrustedProxy settings are configured correctly in admin panel');
} else { print(’✓ No reverse proxy detected (or properly configured)’); }
return { ‘behindProxy’: proxyHeader.isNotEmpty, ‘proxyHeader’: proxyHeader.isNotEmpty ? proxyHeader : null, ‘realIP’: health.data[‘realIP’] ?? null, }; } ```
6. Pre-Flight Checks
```dart
Future<Map<String, dynamic» preFlightCheck() async {
final checks = <String, dynamic>{
‘serverHealthy’: false,
‘canBackup’: false,
‘storageConfigured’: false,
‘issues’:
try { // Basic health check final health = await pb.health.check(); checks[‘serverHealthy’] = health.code == 200;
if (!checks['serverHealthy']) {
(checks['issues'] as List<String>).add('Server health check failed');
return checks;
}
// Authenticate as superuser for extended checks
try {
await pb.collection('_superusers').authWithPassword('admin@example.com', 'password');
final detailedHealth = await pb.health.check();
final canBackup = detailedHealth.data['canBackup'] as bool? ?? false;
final requireS3 = detailedHealth.data['requireS3'] as bool? ?? false;
checks['canBackup'] = canBackup;
checks['storageConfigured'] = !requireS3;
if (!canBackup) {
(checks['issues'] as List<String>).add('Backup operations are currently unavailable');
}
if (requireS3) {
(checks['issues'] as List<String>).add('S3 storage is required but may not be configured');
}
} catch (authError) {
(checks['issues'] as List<String>).add('Superuser authentication failed - limited diagnostics available');
}
} catch (e) {
(checks[‘issues’] as List
return checks; }
// Use before critical operations
final checks = await preFlightCheck();
final issues = checks[‘issues’] as List
7. Automated Backup Scheduler
```dart class BackupScheduler { final Bosbase pb;
BackupScheduler(this.pb);
Future
while (DateTime.now().difference(startTime) < maxWait) {
try {
final health = await pb.health.check();
final canBackup = health.data['canBackup'] as bool? ?? false;
if (canBackup) {
return true;
}
print('Backup in progress, waiting...');
await Future.delayed(checkInterval);
} catch (e) {
print('Health check failed: $e');
return false;
}
}
print('Timeout waiting for backup availability');
return false;
}
Future
if (!isAvailable) {
throw Exception('Backup operations are not available');
}
// Create the backup
await pb.backups.create(backupName);
print('Backup "$backupName" created');
} }
// Usage final scheduler = BackupScheduler(pb); await scheduler.scheduleBackup(‘scheduled_backup.zip’); ```
Error Handling
```dart Future<Map<String, dynamic» safeHealthCheck() async { try { final health = await pb.health.check(); return { ‘success’: true, ‘data’: health.toJson(), }; } catch (e) { // Network errors, server down, etc. return { ‘success’: false, ’error’: e.toString(), ‘code’: (e is ClientException) ? e.statusCode : 0, }; } }
// Handle different error scenarios final result = await safeHealthCheck(); if (!result[‘success’] as bool) { final code = result[‘code’] as int; if (code == 0) { print(‘Network error or server unreachable’); } else { print(‘Server returned error: $code’); } } ```
Best Practices
- Monitoring: Use health checks for regular monitoring (e.g., every 30-60 seconds)
- Load Balancers: Configure load balancers to use the health endpoint for health checks
- Pre-flight Checks: Check `canBackup` before initiating backup operations
- Error Handling: Always handle errors gracefully as the server may be down
- Rate Limiting: Don’t poll the health endpoint too frequently (avoid spamming)
- Caching: Consider caching health check results for a few seconds to reduce load
- Logging: Log health check results for troubleshooting and monitoring
- Alerting: Set up alerts for consecutive health check failures
- Superuser Auth: Only authenticate as superuser when you need diagnostic information
- Proxy Configuration: Use `possibleProxyHeader` to detect and configure reverse proxy settings
Response Codes
| Code | Meaning |
|---|---|
| 200 | Server is healthy |
| Network Error | Server is unreachable or down |
Limitations
- No Detailed Metrics: The health endpoint does not provide detailed performance metrics
- Basic Status Only: Returns basic status, not detailed system information
- Superuser Required: Extended diagnostics require superuser authentication
- No Historical Data: Only returns current status, no historical health data
Head Method Support
The health endpoint also supports the HEAD method for lightweight checks:
```dart import ‘package:http/http.dart’ as http;
// Using HEAD method final response = await http.head(Uri.parse(‘http://127.0.0.1:8090/api/health’));
if (response.statusCode == 200) { print(‘Server is healthy’); } ```
Related Documentation
- Backups API - Using `canBackup` to check backup readiness
- Authentication - Superuser authentication
- Settings API - Configuring trusted proxy settings