first commit
This commit is contained in:
85
docs/api-stack/api/api-schemas/src/admin/health.md
Normal file
85
docs/api-stack/api/api-schemas/src/admin/health.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# health
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines Zod schemas for the admin health check endpoint (`GET /admin/health`). The health endpoint exposes a structured view of the service's operational status — both an aggregate roll-up and per-subsystem detail — so that load balancers, container orchestrators, and monitoring dashboards can make informed routing and alerting decisions without parsing unstructured text responses.
|
||||
|
||||
## Features
|
||||
|
||||
- `HealthStatus` enum schema — `"ok" | "degraded" | "down"` covering the full range of operational states
|
||||
- `HealthCheck` object schema — per-subsystem check with status, optional latency measurement, and optional message
|
||||
- `AdminHealthOutput` schema — aggregate `status` plus a `checks` record keyed by subsystem name (non-empty keys)
|
||||
- `latencyMs` validated as a non-negative number — negative latency is not physically meaningful
|
||||
- Full TypeScript inference via `z.infer<>` — no manual interface duplication
|
||||
- Zod v4 compatible
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { AdminHealthOutput } from "@suaveplan/api-schemas/admin";
|
||||
|
||||
const health = AdminHealthOutput.parse({
|
||||
status: "ok",
|
||||
checks: {
|
||||
database: { status: "ok", latencyMs: 3 },
|
||||
cache: { status: "ok", latencyMs: 1 },
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { AdminHealthOutput, HealthStatus } from "@suaveplan/api-schemas/admin";
|
||||
|
||||
async function collectHealth(): Promise<AdminHealthOutput> {
|
||||
const dbLatency = await measureDbPing();
|
||||
const cacheLatency = await measureCachePing();
|
||||
|
||||
const checks = {
|
||||
database: { status: dbLatency < 100 ? "ok" : "degraded" as HealthStatus, latencyMs: dbLatency },
|
||||
cache: { status: cacheLatency < 10 ? "ok" : "degraded" as HealthStatus, latencyMs: cacheLatency },
|
||||
};
|
||||
|
||||
const anyDown = Object.values(checks).some((c) => c.status === "down");
|
||||
const anyDegraded = Object.values(checks).some((c) => c.status === "degraded");
|
||||
const status: HealthStatus = anyDown ? "down" : anyDegraded ? "degraded" : "ok";
|
||||
|
||||
return AdminHealthOutput.parse({ status, checks });
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `HealthStatus`
|
||||
|
||||
Zod enum: `"ok" | "degraded" | "down"`.
|
||||
|
||||
**Type export:** `type HealthStatus = z.infer<typeof HealthStatus>`
|
||||
|
||||
### `HealthCheck`
|
||||
|
||||
Zod object schema — single subsystem check result.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------------|----------------------|--------------------------------------------------|
|
||||
| `status` | `HealthStatus` | Status of this individual check |
|
||||
| `latencyMs` | `number` (≥ 0) | Round-trip latency; omit when not measured |
|
||||
| `message` | `string` | Optional human-readable context |
|
||||
|
||||
**Type export:** `type HealthCheck = z.infer<typeof HealthCheck>`
|
||||
|
||||
### `AdminHealthOutput`
|
||||
|
||||
Zod object schema — health endpoint response.
|
||||
|
||||
| Field | Type | Description |
|
||||
|----------|-------------------------------------|-----------------------------------------------|
|
||||
| `status` | `HealthStatus` | Aggregate service status |
|
||||
| `checks` | `Record<string, HealthCheck>` | Keyed map of subsystem check results |
|
||||
|
||||
**Type export:** `type AdminHealthOutput = z.infer<typeof AdminHealthOutput>`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The aggregate `status` should be the worst of all individual check statuses — if any check is `"down"` the aggregate should be `"down"`; if any is `"degraded"` and none are `"down"` the aggregate should be `"degraded"`. This roll-up logic belongs in the route handler, not the schema. The `checks` record uses `z.record(z.string().min(1), ...)` to enforce non-empty subsystem names, preventing accidental empty-string keys that would make the map structure ambiguous.
|
||||
Reference in New Issue
Block a user