Health Check
Simple endpoint for monitoring application health -- essential for microservices and containerized deployments.
Quick Reference
| Item | Value |
|---|---|
| Package | @venizia/ignis |
| Class | HealthCheckComponent |
| Controller | HealthCheckController |
| Runtimes | Both |
Import Paths
import { HealthCheckComponent, HealthCheckBindingKeys } from '@venizia/ignis';
import type { IHealthCheckOptions } from '@venizia/ignis';Setup
Step 1: Bind Configuration (Optional)
Skip this step to use the default /health path. To customize the path:
import { HealthCheckBindingKeys, IHealthCheckOptions } from '@venizia/ignis';
// In your Application class's preConfigure method
this.bind<IHealthCheckOptions>({
key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS,
}).toValue({
restOptions: { path: '/health-check' },
});Step 2: Register Component
import { HealthCheckComponent } from '@venizia/ignis';
preConfigure(): ValueOrPromise<void> {
// ... optional bindings from Step 1
this.component(HealthCheckComponent);
}Step 3: Use
The health check endpoints are auto-registered -- no injection needed. Once the component is registered, GET /health and POST /health/ping are available immediately.
TIP
If you customized the path in Step 1, the endpoints will be at your custom path instead (e.g., GET /health-check and POST /health-check/ping).
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
restOptions.path | string | '/health' | Base path for health endpoints |
The component uses IHealthCheckOptions bound to HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS. If no custom binding is found, it falls back to:
const DEFAULT_OPTIONS: IHealthCheckOptions = {
restOptions: { path: '/health' },
};IHealthCheckOptions -- Full Reference
interface IHealthCheckOptions {
restOptions: { path: string };
}The path value is applied via @controller({ path }) decorator on HealthCheckController during the component's binding() phase. All controller routes are relative to this base path.
Component Lifecycle
constructor()-- ReceivesBaseApplicationvia@inject({ key: CoreBindings.APPLICATION_INSTANCE }). Callssuper()withinitDefault: { enable: true, container: application }and provides default bindings forHEALTH_CHECK_OPTIONS:
constructor(
@inject({ key: CoreBindings.APPLICATION_INSTANCE }) private application: BaseApplication,
) {
super({
scope: HealthCheckComponent.name,
initDefault: { enable: true, container: application },
bindings: {
[HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS]: Binding.bind<IHealthCheckOptions>({
key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS,
}).toValue(DEFAULT_OPTIONS),
},
});
}The initDefault: { enable: true, container: application } option tells BaseComponent to automatically register all entries in this.bindings into the DI container before binding() is called. This happens inside BaseComponent.configure() via initDefaultBindings(), which iterates over this.bindings and calls container.set() for any key not already bound. This means users who bind their own HEALTH_CHECK_OPTIONS before registering the component will keep their custom value -- the default is only applied if the key is unbound.
binding()-- Reads options from the DI container usingapplication.get()withisOptional: trueand falls back toDEFAULT_OPTIONSvia the??operator. Applies@controller({ path })decorator dynamically toHealthCheckControllerusingReflect.decorate. Registers the controller with the application:
override binding(): ValueOrPromise<void> {
const healthOptions =
this.application.get<IHealthCheckOptions>({
key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS,
isOptional: true,
}) ?? DEFAULT_OPTIONS;
Reflect.decorate(
[controller({ path: healthOptions.restOptions.path })],
HealthCheckController,
);
this.application.controller(HealthCheckController);
}NOTE
The @controller decorator is applied dynamically during binding(), not statically on the class definition. This allows the path to be configured at runtime via DI bindings.
Binding Keys
| Key | Constant | Type | Required | Default |
|---|---|---|---|---|
@app/health-check/options | HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS | IHealthCheckOptions | No | { restOptions: { path: '/health' } } |
NOTE
The component provides a default binding for HEALTH_CHECK_OPTIONS via initDefault. You only need to bind this key if you want to customize the endpoint path. If you do bind it, do so before calling this.component(HealthCheckComponent) -- the initDefaultBindings() check uses isBound() and will skip keys that already exist in the container.
Rest Paths
| Constant | Value | Full Path (default) |
|---|---|---|
HealthCheckRestPaths.ROOT | / | GET /health |
HealthCheckRestPaths.PING | /ping | POST /health/ping |
Rest Path Constants
class HealthCheckRestPaths {
static readonly ROOT = '/'; // GET /health (or custom base path)
static readonly PING = '/ping'; // POST /health/ping (or custom base path + /ping)
}The controller defines two internal route paths via HealthCheckRestPaths. These paths are relative to the base path configured in IHealthCheckOptions.restOptions.path.
API Endpoints
| Method | Path | Description | Response |
|---|---|---|---|
GET | /health | Basic health check | { "status": "ok" } |
POST | /health/ping | Echo test | { "type": "PONG", "date": "...", "message": "..." } |
GET /health
Returns a simple health status object. Used by load balancers, Kubernetes liveness probes, and monitoring tools to verify the application is running.
POST /health/ping
Echoes a message back with a server timestamp. Useful for:
- Verifying end-to-end connectivity
- Measuring round-trip latency
- Testing request body parsing
Request & Response Specifications
GET /health
Response 200:
{
"status": "ok"
}POST /health/ping
Request body:
{
"type": "PING",
"message": "Any string here"
}| Field | Type | Required | Constraints |
|---|---|---|---|
type | string | No | Defaults to "PING" |
message | string | Yes | Min 1, max 255 characters |
Response 200:
{
"type": "PONG",
"date": "2026-02-11T12:00:00.000Z",
"message": "Any string here"
}Route Definition Patterns
The HealthCheckController demonstrates all three route definition patterns supported by Ignis:
| Pattern | Method | Used For |
|---|---|---|
Fluent API (bindRoute().to()) | Root health check (GET /) | Inline handlers, method chaining |
Imperative API (defineRoute()) | (commented out in source) | Direct handler assignment in binding() |
Decorator API (@api()) | Ping endpoint (POST /ping) | Class method handlers with OpenAPI metadata |
The controller uses this.definitions = RouteConfigs to store route configurations for introspection. This is optional but enables tools to discover available routes without invoking binding().
Controller Source
import {
BaseRestController, IControllerOptions, TRouteContext,
} from '@venizia/ignis';
import { api, jsonContent, jsonResponse } from '@venizia/ignis';
import { z } from '@hono/zod-openapi';
import { HTTP, ValueOrPromise } from '@venizia/ignis-helpers';
const RouteConfigs = {
ROOT: {
method: HTTP.Methods.GET,
path: HealthCheckRestPaths.ROOT,
responses: jsonResponse({
schema: z.object({ status: z.string() }).openapi({
description: 'HealthCheck Schema',
examples: [{ status: 'ok' }],
}),
description: 'Health check status',
}),
},
PING: {
method: HTTP.Methods.POST,
path: HealthCheckRestPaths.PING,
request: {
body: jsonContent({
description: 'PING | Request body',
schema: z.object({
type: z.string().optional().default('PING'),
message: z.string().min(1).max(255),
}),
}),
},
responses: jsonResponse({
schema: z
.object({
type: z.string().optional().default('PONG'),
date: z.iso.datetime(),
message: z.string(),
})
.openapi({
description: 'HealthCheck PingPong Schema',
examples: [{ date: new Date().toISOString(), message: 'ok' }],
}),
description: 'HealthCheck PingPong Message',
}),
},
} as const;
export class HealthCheckController extends BaseRestController {
constructor(opts: IControllerOptions) {
super({ ...opts, scope: HealthCheckController.name });
this.definitions = RouteConfigs;
}
override binding(): ValueOrPromise<void> {
// Fluent API for the root health check
this.bindRoute({ configs: RouteConfigs.ROOT }).to({
handler: context => {
return context.json({ status: 'ok' }, HTTP.ResultCodes.RS_2.Ok);
},
});
}
// Decorator API for the ping endpoint
@api({ configs: RouteConfigs.PING })
pingPong(context: TRouteContext) {
const { message } = context.req.valid<{ type?: string; message: string }>('json');
return context.json(
{ type: 'PONG', date: new Date().toISOString(), message },
HTTP.ResultCodes.RS_2.Ok,
);
}
}Route Configuration Schemas
Root endpoint (GET /):
const RouteConfigs = {
ROOT: {
method: HTTP.Methods.GET,
path: HealthCheckRestPaths.ROOT,
responses: jsonResponse({
schema: z.object({ status: z.string() }).openapi({
description: 'HealthCheck Schema',
examples: [{ status: 'ok' }],
}),
description: 'Health check status',
}),
},Ping endpoint (POST /ping):
PING: {
method: HTTP.Methods.POST,
path: HealthCheckRestPaths.PING,
request: {
body: jsonContent({
description: 'PING | Request body',
schema: z.object({
type: z.string().optional().default('PING'),
message: z.string().min(1).max(255),
}),
}),
},
responses: jsonResponse({
schema: z.object({
type: z.string().optional().default('PONG'),
date: z.iso.datetime(),
message: z.string(),
}).openapi({
description: 'HealthCheck PingPong Schema',
examples: [{ date: new Date().toISOString(), message: 'ok' }],
}),
description: 'HealthCheck PingPong Message',
}),
},
} as const;Troubleshooting
"Health check endpoint returns 404"
Cause: The HealthCheckComponent was not registered in your application, or it was registered after controllers were already mounted.
Fix: Register the component in preConfigure() before any controller registration:
preConfigure(): ValueOrPromise<void> {
this.component(HealthCheckComponent);
// ... other registrations
}"Custom path is not applied"
Cause: The custom IHealthCheckOptions binding was registered after the component. The component reads options during its binding() phase -- if no binding exists at that point, it uses the default /health path. However, because HealthCheckComponent uses initDefault: { enable: true }, it also registers a default binding before binding() runs. If you bind your custom options after calling this.component(), the default has already been set and your override will not take effect during the current lifecycle.
Fix: Bind your custom options before calling this.component(HealthCheckComponent):
preConfigure(): ValueOrPromise<void> {
// Bind options FIRST
this.bind<IHealthCheckOptions>({
key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS,
}).toValue({
restOptions: { path: '/custom-health' },
});
// THEN register component
this.component(HealthCheckComponent);
}"POST /health/ping returns validation error"
Cause: The request body is missing the required message field, or the message value exceeds the 255-character limit.
Fix: Ensure the request body includes a message string between 1 and 255 characters:
{
"message": "hello"
}See Also
Guides:
- Components Overview - Component system basics
- Application - Registering components
Components:
- All Components - Built-in components list
Best Practices:
- Deployment Strategies - Production monitoring