Health API - C# 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:
using Bosbase;
var client = new BosbaseClient("http://127.0.0.1:8090");
// Basic health check (no auth required)
var health = await client.Health.CheckAsync();
For additional diagnostic information, authenticate as a superuser:
// Authenticate as superuser for extended health data
await client.Collection("_superusers").AuthWithPasswordAsync("admin@example.com", "password");
var health = await client.Health.CheckAsync();
Health Check Response Structure
Basic Response (Guest/Regular User)
{
"code": 200,
"message": "API is healthy.",
"data": {}
}
Superuser Response
{
"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
// Simple health check
var health = await client.Health.CheckAsync();
var message = health["message"]?.ToString(); // "API is healthy."
var code = health["code"]; // 200
With Superuser Authentication
// Authenticate as superuser first
await client.Collection("_superusers").AuthWithPasswordAsync("admin@example.com", "password");
// Get extended health information
var health = await client.Health.CheckAsync();
var data = health["data"] as Dictionary<string, object?>;
var canBackup = data?["canBackup"]; // true/false
var realIP = data?["realIP"]?.ToString(); // "192.168.1.100"
var requireS3 = data?["requireS3"]; // false
var possibleProxyHeader = data?["possibleProxyHeader"]?.ToString(); // "" 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 |
Dictionary | 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
async Task<bool> CheckServerHealth()
{
try
{
var health = await client.Health.CheckAsync();
var code = health["code"];
var message = health["message"]?.ToString();
if (code != null && code.ToString() == "200" && message == "API is healthy.")
{
Console.WriteLine("✓ Server is healthy");
return true;
}
else
{
Console.WriteLine("✗ Server health check failed");
return false;
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"✗ Health check error: {ex.Message}");
return false;
}
}
// Use in monitoring
var timer = new System.Timers.Timer(60000); // Check every minute
timer.Elapsed += async (sender, e) =>
{
var isHealthy = await CheckServerHealth();
if (!isHealthy)
{
Console.Warn("Server health check failed!");
}
};
timer.Start();
2. Backup Readiness Check
async Task<bool> CanPerformBackup()
{
try
{
// Authenticate as superuser
await client.Collection("_superusers").AuthWithPasswordAsync("admin@example.com", "password");
var health = await client.Health.CheckAsync();
var data = health["data"] as Dictionary<string, object?>;
var canBackup = data?["canBackup"];
if (canBackup != null && canBackup.ToString() == "False")
{
Console.WriteLine("⚠️ Backup operation is currently in progress");
return false;
}
Console.WriteLine("✓ Backup operations are allowed");
return true;
}
catch (Exception ex)
{
Console.Error.WriteLine($"Failed to check backup readiness: {ex.Message}");
return false;
}
}
// Use before creating backups
if (await CanPerformBackup())
{
await client.Backups.CreateAsync("backup.zip");
}
3. Monitoring Dashboard
class HealthMonitor
{
private readonly BosbaseClient _client;
private bool _isSuperuser = false;
public HealthMonitor(BosbaseClient client)
{
_client = client;
}
public async Task<bool> AuthenticateAsSuperuser(string email, string password)
{
try
{
await _client.Collection("_superusers").AuthWithPasswordAsync(email, password);
_isSuperuser = true;
return true;
}
catch (Exception ex)
{
Console.Error.WriteLine($"Superuser authentication failed: {ex.Message}");
return false;
}
}
public async Task<Dictionary<string, object?>> GetHealthStatus()
{
try
{
var health = await _client.Health.CheckAsync();
var status = new Dictionary<string, object?>
{
["healthy"] = health["code"]?.ToString() == "200",
["message"] = health["message"],
["timestamp"] = DateTime.UtcNow.ToString("O"),
};
if (_isSuperuser && health["data"] is Dictionary<string, object?> data)
{
status["diagnostics"] = new Dictionary<string, object?>
{
["canBackup"] = data.GetValueOrDefault("canBackup"),
["realIP"] = data.GetValueOrDefault("realIP"),
["requireS3"] = data.GetValueOrDefault("requireS3"),
["behindProxy"] = !string.IsNullOrEmpty(data.GetValueOrDefault("possibleProxyHeader")?.ToString()),
["proxyHeader"] = data.GetValueOrDefault("possibleProxyHeader"),
};
}
return status;
}
catch (Exception ex)
{
return new Dictionary<string, object?>
{
["healthy"] = false,
["error"] = ex.Message,
["timestamp"] = DateTime.UtcNow.ToString("O"),
};
}
}
public void StartMonitoring(int intervalMs = 60000)
{
var timer = new System.Timers.Timer(intervalMs);
timer.Elapsed += async (sender, e) =>
{
var status = await GetHealthStatus();
Console.WriteLine($"Health Status: {System.Text.Json.JsonSerializer.Serialize(status)}");
if (status["healthy"]?.ToString() != "True")
{
OnHealthIssue(status);
}
};
timer.Start();
}
private void OnHealthIssue(Dictionary<string, object?> status)
{
Console.Error.WriteLine($"Health issue detected: {System.Text.Json.JsonSerializer.Serialize(status)}");
// Implement alerting logic here
}
}
// Usage
var monitor = new HealthMonitor(client);
await monitor.AuthenticateAsSuperuser("admin@example.com", "password");
monitor.StartMonitoring(30000); // Check every 30 seconds
4. Load Balancer Health Check
// Simple health check for load balancers
async Task<bool> SimpleHealthCheck()
{
try
{
var health = await client.Health.CheckAsync();
return health["code"]?.ToString() == "200";
}
catch
{
return false;
}
}
// Use in ASP.NET Core route for load balancer
app.MapGet("/health", async () =>
{
var isHealthy = await SimpleHealthCheck();
return isHealthy
? Results.Ok(new { status = "healthy" })
: Results.StatusCode(503);
});
5. Proxy Detection
async Task<Dictionary<string, object?>> CheckProxySetup()
{
await client.Collection("_superusers").AuthWithPasswordAsync("admin@example.com", "password");
var health = await client.Health.CheckAsync();
var data = health["data"] as Dictionary<string, object?>;
var proxyHeader = data?["possibleProxyHeader"]?.ToString();
if (!string.IsNullOrEmpty(proxyHeader))
{
Console.WriteLine("⚠️ Server appears to be behind a reverse proxy");
Console.WriteLine($" Detected proxy header: {proxyHeader}");
Console.WriteLine($" Real IP: {data?["realIP"]}");
// Provide guidance on trusted proxy configuration
Console.WriteLine(" Ensure TrustedProxy settings are configured correctly in admin panel");
}
else
{
Console.WriteLine("✓ No reverse proxy detected (or properly configured)");
}
return new Dictionary<string, object?>
{
["behindProxy"] = !string.IsNullOrEmpty(proxyHeader),
["proxyHeader"] = proxyHeader,
["realIP"] = data?["realIP"],
};
}
6. Pre-Flight Checks
async Task<Dictionary<string, object?>> PreFlightCheck()
{
var checks = new Dictionary<string, object?>
{
["serverHealthy"] = false,
["canBackup"] = false,
["storageConfigured"] = false,
["issues"] = new List<string>()
};
try
{
// Basic health check
var health = await client.Health.CheckAsync();
checks["serverHealthy"] = health["code"]?.ToString() == "200";
if (checks["serverHealthy"]?.ToString() != "True")
{
((List<string>)checks["issues"]!).Add("Server health check failed");
return checks;
}
// Authenticate as superuser for extended checks
try
{
await client.Collection("_superusers").AuthWithPasswordAsync("admin@example.com", "password");
var detailedHealth = await client.Health.CheckAsync();
var data = detailedHealth["data"] as Dictionary<string, object?>;
checks["canBackup"] = data?["canBackup"]?.ToString() == "True";
var requireS3 = data?["requireS3"]?.ToString() == "True";
checks["storageConfigured"] = !requireS3;
if (checks["canBackup"]?.ToString() != "True")
{
((List<string>)checks["issues"]!).Add("Backup operations are currently unavailable");
}
if (requireS3)
{
((List<string>)checks["issues"]!).Add("S3 storage is required but may not be configured");
}
}
catch (Exception authEx)
{
((List<string>)checks["issues"]!).Add("Superuser authentication failed - limited diagnostics available");
}
}
catch (Exception ex)
{
((List<string>)checks["issues"]!).Add($"Health check error: {ex.Message}");
}
return checks;
}
// Use before critical operations
var checks = await PreFlightCheck();
if (checks["issues"] is List<string> issues && issues.Count > 0)
{
Console.Warn($"Pre-flight check issues: {string.Join(", ", issues)}");
// Handle issues before proceeding
}
7. Automated Backup Scheduler
class BackupScheduler
{
private readonly BosbaseClient _client;
public BackupScheduler(BosbaseClient client)
{
_client = client;
}
public async Task<bool> WaitForBackupAvailability(int maxWaitMs = 300000)
{
var startTime = DateTime.UtcNow;
var checkInterval = 5000; // Check every 5 seconds
while ((DateTime.UtcNow - startTime).TotalMilliseconds < maxWaitMs)
{
try
{
var health = await _client.Health.CheckAsync();
var data = health["data"] as Dictionary<string, object?>;
if (data?["canBackup"]?.ToString() == "True")
{
return true;
}
Console.WriteLine("Backup in progress, waiting...");
await Task.Delay(checkInterval);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Health check failed: {ex.Message}");
return false;
}
}
Console.Error.WriteLine("Timeout waiting for backup availability");
return false;
}
public async Task ScheduleBackup(string backupName)
{
// Wait for backup operations to be available
var isAvailable = await WaitForBackupAvailability();
if (!isAvailable)
{
throw new Exception("Backup operations are not available");
}
// Create the backup
await _client.Backups.CreateAsync(backupName);
Console.WriteLine($"Backup \"{backupName}\" created");
}
}
// Usage
var scheduler = new BackupScheduler(client);
await scheduler.ScheduleBackup("scheduled_backup.zip");
Error Handling
async Task<Dictionary<string, object?>> SafeHealthCheck()
{
try
{
var health = await client.Health.CheckAsync();
return new Dictionary<string, object?>
{
["success"] = true,
["data"] = health,
};
}
catch (Exception ex)
{
// Network errors, server down, etc.
return new Dictionary<string, object?>
{
["success"] = false,
["error"] = ex.Message,
["code"] = 0,
};
}
}
// Handle different error scenarios
var result = await SafeHealthCheck();
if (result["success"]?.ToString() != "True")
{
var code = result["code"]?.ToString();
if (code == "0")
{
Console.Error.WriteLine("Network error or server unreachable");
}
else
{
Console.Error.WriteLine($"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
canBackupbefore 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
possibleProxyHeaderto 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:
// Using HEAD method (if supported by your HTTP client)
using var httpClient = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Head, "http://127.0.0.1:8090/api/health");
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Server is healthy");
}
Related Documentation
- Backups API - Using
canBackupto check backup readiness - Authentication - Superuser authentication
- Settings API - Configuring trusted proxy settings