Health API - Java 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:
import com.bosbase.sdk.BosBase;
BosBase pb = new BosBase("http://127.0.0.1:8090");
// Basic health check (no auth required)
ObjectNode health = pb.health.check();
For additional diagnostic information, authenticate as a superuser:
// Authenticate as superuser for extended health data
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
ObjectNode health = pb.health.check();
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": boolean, // Whether backup operations are allowed
"realIP": string, // Real IP address of the client
"requireS3": boolean, // 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
ObjectNode health = pb.health.check();
System.out.println(health.get("message").asText()); // "API is healthy."
System.out.println(health.get("code").asInt()); // 200
With Superuser Authentication
// Authenticate as superuser first
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
// Get extended health information
ObjectNode health = pb.health.check();
ObjectNode data = (ObjectNode) health.get("data");
System.out.println(data.get("canBackup").asBoolean()); // true/false
System.out.println(data.get("realIP").asText()); // "192.168.1.100"
System.out.println(data.get("requireS3").asBoolean()); // false
System.out.println(data.get("possibleProxyHeader").asText()); // "" or header name
Response Fields
Common Fields (All Users)
| Field | Type | Description |
|---|---|---|
code |
number | HTTP status code (always 200 for healthy server) |
message |
string | Health status message (“API is healthy.”) |
data |
object | Health data (empty for non-superusers, populated for superusers) |
Superuser-Only Fields (in data)
| Field | Type | Description |
|---|---|---|
canBackup |
boolean | 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 |
boolean | 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
public boolean checkServerHealth() {
try {
ObjectNode health = pb.health.check();
if (health.get("code").asInt() == 200 &&
"API is healthy.".equals(health.get("message").asText())) {
System.out.println("✓ Server is healthy");
return true;
} else {
System.out.println("✗ Server health check failed");
return false;
}
} catch (Exception e) {
System.err.println("✗ Health check error: " + e.getMessage());
return false;
}
}
// Use in monitoring
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
boolean isHealthy = checkServerHealth();
if (!isHealthy) {
System.err.println("Server health check failed!");
}
}, 0, 60, TimeUnit.SECONDS); // Check every minute
2. Backup Readiness Check
public boolean canPerformBackup() {
try {
// Authenticate as superuser
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
ObjectNode health = pb.health.check();
ObjectNode data = (ObjectNode) health.get("data");
if (data.has("canBackup") && !data.get("canBackup").asBoolean()) {
System.out.println("⚠️ Backup operation is currently in progress");
return false;
}
System.out.println("✓ Backup operations are allowed");
return true;
} catch (Exception e) {
System.err.println("Failed to check backup readiness: " + e.getMessage());
return false;
}
}
// Use before creating backups
if (canPerformBackup()) {
pb.backups.create("backup.zip", null);
}
3. Monitoring Dashboard
public class HealthMonitor {
private BosBase pb;
private boolean isSuperuser = false;
public HealthMonitor(BosBase pb) {
this.pb = pb;
}
public boolean authenticateAsSuperuser(String email, String password) {
try {
pb.admins().authWithPassword(email, password, null, null, null, null, null, null, null, null);
this.isSuperuser = true;
return true;
} catch (Exception e) {
System.err.println("Superuser authentication failed: " + e.getMessage());
return false;
}
}
public Map<String, Object> getHealthStatus() {
try {
ObjectNode health = pb.health.check();
Map<String, Object> status = new HashMap<>();
status.put("healthy", health.get("code").asInt() == 200);
status.put("message", health.get("message").asText());
status.put("timestamp", System.currentTimeMillis());
if (isSuperuser && health.has("data")) {
ObjectNode data = (ObjectNode) health.get("data");
Map<String, Object> diagnostics = new HashMap<>();
diagnostics.put("canBackup", data.has("canBackup") ? data.get("canBackup").asBoolean() : null);
diagnostics.put("realIP", data.has("realIP") ? data.get("realIP").asText() : null);
diagnostics.put("requireS3", data.has("requireS3") ? data.get("requireS3").asBoolean() : null);
diagnostics.put("behindProxy", data.has("possibleProxyHeader") &&
!data.get("possibleProxyHeader").asText().isEmpty());
diagnostics.put("proxyHeader", data.has("possibleProxyHeader") ?
data.get("possibleProxyHeader").asText() : null);
status.put("diagnostics", diagnostics);
}
return status;
} catch (Exception e) {
Map<String, Object> status = new HashMap<>();
status.put("healthy", false);
status.put("error", e.getMessage());
status.put("timestamp", System.currentTimeMillis());
return status;
}
}
public void startMonitoring(long intervalMs) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
Map<String, Object> status = getHealthStatus();
System.out.println("Health Status: " + status);
if (!(Boolean) status.get("healthy")) {
onHealthIssue(status);
}
}, 0, intervalMs, TimeUnit.MILLISECONDS);
}
private void onHealthIssue(Map<String, Object> status) {
System.err.println("Health issue detected: " + status);
// Implement alerting logic here
}
}
// Usage
HealthMonitor monitor = new HealthMonitor(pb);
monitor.authenticateAsSuperuser("admin@example.com", "password");
monitor.startMonitoring(30000); // Check every 30 seconds
4. Load Balancer Health Check
// Simple health check for load balancers
public boolean simpleHealthCheck() {
try {
ObjectNode health = pb.health.check();
return health.get("code").asInt() == 200;
} catch (Exception e) {
return false;
}
}
// Use in web application endpoint
@GetMapping("/health")
public ResponseEntity<Map<String, String>> health() {
boolean isHealthy = simpleHealthCheck();
Map<String, String> response = new HashMap<>();
if (isHealthy) {
response.put("status", "healthy");
return ResponseEntity.ok(response);
} else {
response.put("status", "unhealthy");
return ResponseEntity.status(503).body(response);
}
}
5. Proxy Detection
public Map<String, Object> checkProxySetup() throws Exception {
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
ObjectNode health = pb.health.check();
ObjectNode data = (ObjectNode) health.get("data");
String proxyHeader = data.has("possibleProxyHeader") ?
data.get("possibleProxyHeader").asText() : "";
if (!proxyHeader.isEmpty()) {
System.out.println("⚠️ Server appears to be behind a reverse proxy");
System.out.println(" Detected proxy header: " + proxyHeader);
System.out.println(" Real IP: " + data.get("realIP").asText());
System.out.println(" Ensure TrustedProxy settings are configured correctly in admin panel");
} else {
System.out.println("✓ No reverse proxy detected (or properly configured)");
}
Map<String, Object> result = new HashMap<>();
result.put("behindProxy", !proxyHeader.isEmpty());
result.put("proxyHeader", proxyHeader.isEmpty() ? null : proxyHeader);
result.put("realIP", data.has("realIP") ? data.get("realIP").asText() : null);
return result;
}
6. Pre-Flight Checks
public Map<String, Object> preFlightCheck() {
Map<String, Object> checks = new HashMap<>();
checks.put("serverHealthy", false);
checks.put("canBackup", false);
checks.put("storageConfigured", false);
List<String> issues = new ArrayList<>();
checks.put("issues", issues);
try {
// Basic health check
ObjectNode health = pb.health.check();
checks.put("serverHealthy", health.get("code").asInt() == 200);
if (!(Boolean) checks.get("serverHealthy")) {
issues.add("Server health check failed");
return checks;
}
// Authenticate as superuser for extended checks
try {
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
ObjectNode detailedHealth = pb.health.check();
ObjectNode data = (ObjectNode) detailedHealth.get("data");
checks.put("canBackup", data.has("canBackup") && data.get("canBackup").asBoolean());
checks.put("storageConfigured", !data.has("requireS3") || !data.get("requireS3").asBoolean());
if (!(Boolean) checks.get("canBackup")) {
issues.add("Backup operations are currently unavailable");
}
if (data.has("requireS3") && data.get("requireS3").asBoolean()) {
issues.add("S3 storage is required but may not be configured");
}
} catch (Exception authError) {
issues.add("Superuser authentication failed - limited diagnostics available");
}
} catch (Exception e) {
issues.add("Health check error: " + e.getMessage());
}
return checks;
}
// Use before critical operations
Map<String, Object> checks = preFlightCheck();
@SuppressWarnings("unchecked")
List<String> issues = (List<String>) checks.get("issues");
if (!issues.isEmpty()) {
System.err.println("Pre-flight check issues: " + issues);
// Handle issues before proceeding
}
7. Automated Backup Scheduler
public class BackupScheduler {
private BosBase pb;
public BackupScheduler(BosBase pb) {
this.pb = pb;
}
public boolean waitForBackupAvailability(long maxWaitMs) {
long startTime = System.currentTimeMillis();
long checkInterval = 5000; // Check every 5 seconds
while (System.currentTimeMillis() - startTime < maxWaitMs) {
try {
ObjectNode health = pb.health.check();
ObjectNode data = (ObjectNode) health.get("data");
if (data.has("canBackup") && data.get("canBackup").asBoolean()) {
return true;
}
System.out.println("Backup in progress, waiting...");
Thread.sleep(checkInterval);
} catch (Exception e) {
System.err.println("Health check failed: " + e.getMessage());
return false;
}
}
System.err.println("Timeout waiting for backup availability");
return false;
}
public void scheduleBackup(String backupName) throws Exception {
// Wait for backup operations to be available
boolean isAvailable = waitForBackupAvailability(300000); // 5 minutes max
if (!isAvailable) {
throw new RuntimeException("Backup operations are not available");
}
// Create the backup
pb.backups.create(backupName, null);
System.out.println("Backup \"" + backupName + "\" created");
}
}
// Usage
BackupScheduler scheduler = new BackupScheduler(pb);
scheduler.scheduleBackup("scheduled_backup.zip");
Error Handling
public Map<String, Object> safeHealthCheck() {
try {
ObjectNode health = pb.health.check();
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("data", health);
return result;
} catch (Exception e) {
// Network errors, server down, etc.
Map<String, Object> result = new HashMap<>();
result.put("success", false);
result.put("error", e.getMessage());
result.put("code", 0);
return result;
}
}
// Handle different error scenarios
Map<String, Object> result = safeHealthCheck();
if (!(Boolean) result.get("success")) {
int code = (Integer) result.get("code");
if (code == 0) {
System.err.println("Network error or server unreachable");
} else {
System.err.println("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
Related Documentation
- Backups API - Using
canBackupto check backup readiness - Authentication - Superuser authentication
- Settings API - Configuring trusted proxy settings