Putnami
DocsGitHub

Licensed under FSL-1.1-MIT

Getting Started
Concepts
How To
Build A Web App
Build An Api Service
Share Code Between Projects
Configure Your App
Add Persistence
Add Authentication
Add Background Jobs
Develop With Ai
Structure Business Logic With Di
Upgrade Putnami
Principles
Tooling & Workspace
Workspace
Cli
Jobs & Caching
Extensions
Templates
Error Handling
Frameworks
Typescript
ExtensionOverviewWebReact RoutingForms And ActionsStatic FilesApiErrors And ResponsesConfigurationLoggingHttp And MiddlewareDependency InjectionPlugins And LifecycleSessionsAuthPersistenceDocumentEventsStorageCachingWebsocketsTestingHealth ChecksTelemetryProto GrpcSmart ClientSchemaPlatform Endpoints
Go
ExtensionOverviewHttpDependency InjectionPlugins And LifecycleConfigurationSecurityPersistenceErrorsEventsStorageCachingLoggingTelemetryGrpcService ClientsValidationOpenapiTestingPlatform Endpoints
Python
Extension
Platform
Ci
  1. DocsSeparator
  2. FrameworksSeparator
  3. TypescriptSeparator
  4. Platform Endpoints

Platform endpoints

@putnami/application's platform() plugin mounts the standard operational HTTP surface every workload needs — liveness, readiness, version metadata — and auto-discovers per-plugin probes from the module tree.

It replaces the per-workload boilerplate (and the legacy health() plugin's single /_/health endpoint) with one plugin you opt in to.

Plugin setup

import { application, http, platform } from '@putnami/application';
import { sql } from '@putnami/database';

const app = application()
  .use(sql())                    // implements HealthChecker
  .use(http({ port: 3000 }))
  .use(platform({
    version: { name: 'my-service', version: '1.0.0' },
  }));

await app.start();

GET /healthz now reports:

{
  "status": "ok",
  "checks": { "database": "ok" }
}

— the database probe shows up automatically because sql() implements HealthChecker.

Endpoints

Path Purpose
/livez Lightweight liveness — 200 {"status":"ok"} whenever the handler can run. No probes, no flags. Safe for Kubernetes liveness probes.
/healthz Liveness aggregate — 200 when running and every HealthChecker probe passes; 503 {"status":"unavailable"} before start() and after stop(); 503 {"status":"degraded","checks":{…}} when any probe fails.
/readyz Readiness aggregate — same shape as /healthz, driven by ReadinessChecker probes. The endpoint to use as a Kubernetes readiness probe.
/version Build metadata as JSON. Returns the VersionInfo you configure; empty fields are omitted.

Default mount is root. Set prefix to namespace (prefix: '/_' mounts /_/healthz, /_/livez, …).

Capability interfaces

The platform plugin discovers two interfaces by walking the module tree from the root and registers each implementation under its name.

import type { HealthChecker, ReadinessChecker } from '@putnami/application';

class CacheClient implements Plugin, HealthChecker {
  readonly name = 'cache';

  async checkHealth(signal: AbortSignal): Promise<void> {
    await this.ping(signal); // throws on failure
  }
}
Interface Semantics Failure consequence (k8s)
HealthChecker Dependency is broken in a way only a restart fixes Pod restart
ReadinessChecker Dependency is temporarily down — drain traffic but don't restart Traffic drained, no restart

A single plugin can implement both: checkHealth contributes to /healthz, checkReadiness contributes to /readyz.

Implementations must be safe to call concurrently and respect AbortSignal cancellation. The plugin enforces a per-request probeTimeoutMs (default 5000) so a hung probe can't stall the endpoint.

Built-in contributor: @putnami/database's sql() plugin implements HealthChecker — SELECT 1 against the default datasource shows up under the database key on /healthz automatically when both plugins are mounted.

Explicit probes

For probes not owned by a plugin (an external URL, an ad-hoc check), register directly:

const platformPlugin = platform()
  .addHealthChecker('upstream', async (signal) => {
    const res = await fetch('https://example.com/health', { signal });
    if (!res.ok) throw new Error(`upstream returned ${res.status}`);
  })
  .addReadinessChecker('warm', async () => {
    if (!cacheWarmed) throw new Error('cache warming');
  });

Explicit registrations win over auto-discovered probes of the same name.

Configuration

platform({
  prefix: '/_',                            // default ''
  probeTimeoutMs: 3_000,                   // default 5_000
  version: { name: 'my-service', version: '1.0.0' },
});

Cross-language contract

The endpoint paths, response envelope, status mapping, capability semantics, probe behaviour, and discovery rules are pinned by the platform protocol. Both the Go and TS runtimes ship against the same contract — the TS protocol validators (validateEnvelope, validateProbeName, validatePrefix) are exposed for application-level conformance tests.

Migrating from health()

// before
app.use(http()).use(health());

// after
app.use(http()).use(platform({ prefix: '/_' })); // keep /_ namespace

/_/health becomes /_/healthz, and you also get /_/livez, /_/readyz, /_/version. Drop the prefix for plain Kubernetes-style paths (/healthz, /livez, …).

On this page

  • Platform endpoints
  • Plugin setup
  • Endpoints
  • Capability interfaces
  • Explicit probes
  • Configuration
  • Cross-language contract
  • Migrating from health()