PrimeBrickPrimeBrick
  • Docs
  • Contact
  • MIT License
  • Documentation
  • MCP Server
  • API Catalog
  • Services
  • Libraries
PrimeBrickPrimeBrick

© 2026 PrimeBrick. MIT License. v3.11.2

github
DAL Library
SDK Library
    Developer Guide
      OverviewGetting StartedAnatomy of a MicroserviceAuthenticationClient Shield & Rotating SecretsHTTP ServerRPC RouterEntity RouterOpenAPI GenerationDocs PackConfig tables & ConfigLoaderDatabase MigrationsTranslationsCurrency UtilitiesNATS™ ClientService RegistrationSSE StandardPresenceExt-JSONRedis cache layerTelemetry & LoggingAPI Reference
Decimal Library
powered by Zudoku
Developer Guide

Service Registration

ServiceRegistrar registers a microservice via NATS™ lifecycle events and maintains a heartbeat. The BE subscribes to these events and persists the state to the service_registry table. The microservice never touches the database directly.

Lifecycle events

Three NATS™ subjects carry lifecycle events:

SubjectWhenPayload
service.registerOn startup + on gateway restartServiceRegisterPayload — code, base_url, endpoints, health, metadata, capabilities
service.heartbeatEvery 30s (default)ServiceHeartbeatPayload — code, base_url, health, metadata
service.unregisterOn graceful shutdownServiceUnregisterPayload — code, base_url
service.staleWatchdog-detected silenceServiceStalePayload
service.gateway_onlineBE startup (broadcast){ at } — every live service re-registers

Gateway restart re-registration

service.register is a request/reply handshake: the registrar retries every 5 s until the BE acks { registered: true } — a missing/negative ack means no heartbeats. After the first successful registration the service subscribes to service.gateway_online: when the BE restarts it announces itself and every live service re-registers, so the gateway re-ingests identity/endpoints/capabilities and re-discovers OpenAPI without polling.

service.gateway_online is a broadcast — deliberately NOT a queue group — because every running service instance must observe it.

These subjects are defined in the SERVICE_SUBJECTS constant.

Usage

Code
import { NatsClient, ServiceRegistrar } from "@primebrick/sdk"; const registrar = new ServiceRegistrar(NatsClient, { serviceCode: "emailsender", baseUrl: "http://emailsender:3001", endpoints: { "POST /send": {} }, name: "Email Sender", description: "Sends emails via configured providers", author: "Primebrick", github_repo_url: "https://github.com/michaelsogos/primebrick-us-v3", service_version: "1.0.0", is_behind_scaler: true, }, async () => ({ http_healthy: true, checks: { nats: { ok: NatsClient.isConnected() }, db: { ok: await dbPing() }, }, })); // Register on startup await registrar.register(); // Start heartbeat loop (30s interval) registrar.startHeartbeat(); // On graceful shutdown await registrar.unregister(); registrar.stopHeartbeat();

Nav declaration — validated at enrollment

A module declares its frontend surface in endpoints.nav (a ModuleNavDeclaration): route_prefixes, nav links, and the routes census. At service.register the BE runs validateNavDeclaration(code, decl, partOf) and rejects the enrollment on any violation — the declaration never reaches service_registry.

The enforced conventions:

RuleContract
Primary prefixroute_prefixes[0] must be /{part_of}/{code}; extra prefixes keep code as the URL's second segment
R1 — list pluralkind:"list" routes end in a plural leaf (pluralize)
R2 — nav leafa leaf nav href resolves to a declared route, or is itself plural
R3 — derived leaveskind:"create" ends in /create; kind:"detail" ends in [param]
accessoptional access.{app}.{module}.{page} gate — must prefix-match the module's own grant

Route paths are derived, never literals — entityPaths(prefix, listLeaf, detailParam?) is the single constructor of the entity-route shape:

Code
import { entityPaths, type ModuleNavDeclaration } from "@primebrick/sdk"; const customersPaths = entityPaths("/crm", "customers"); // → list "/crm/customers" // → create "/crm/customers/create" // → detail "/crm/customers/[uuid]" (detailParam → "[code]" etc.) export const CRM_NAV: ModuleNavDeclaration = { route_prefixes: ["/crm"], nav: [{ id: "customers", label_key: "crm.customers.nav", href: customersPaths.list }], routes: [ { route: customersPaths.list, kind: "list", entity: "customer" }, { route: customersPaths.create, kind: "create", entity: "customer" }, ], };

Unit-test the declaration at write time — the same function the BE calls at enrollment:

Code
import { validateNavDeclaration } from "@primebrick/sdk"; expect(validateNavDeclaration("crm", CRM_NAV, "crm")).toBeNull();

Health checks

The healthCheckFn (third constructor argument) is called on each heartbeat to include the current health status. It returns http_healthy (boolean) and checks (a record of named checks with ok and optional error).

If no healthCheckFn is provided, the registrar defaults to http_healthy: true with empty checks.

Configuration options

ServiceRegistrarConfig supports:

FieldRequiredDescription
serviceCodeyesUnique service identifier
baseUrlyesService URL (used by BE for direct mode routing)
endpointsyesMap of endpoint signatures
heartbeatIntervalMsnoHeartbeat interval (default: 30000)
namenoDisplay name
descriptionnoService description
authornoAuthor name
github_repo_urlnoRepository URL
service_versionnoSemver string
is_behind_scalernoWhether the service is behind a scaler (default: false)
icon / icon_typenoIcon for UI display

ServiceRegistryPort

The BE implements ServiceRegistryPort to persist lifecycle events. The port interface supports both scaler mode (one row per code) and direct mode (multiple rows per code, distinguished by base_url):

MethodModeDescription
findByCode(code)bothFind one row by code
findByCodeAndBaseUrl(code, baseUrl)directFind by code + URL
findAllByCode(code)directAll rows for a code
findAll()bothAll rows
insert(row)bothInsert new row
updateByCode(code, row)scalerUpdate by code
updateByCodeAndBaseUrl(code, baseUrl, row)directUpdate by code + URL
deleteByCodeAndBaseUrl(code, baseUrl)directDelete by code + URL

Next steps

  • NATS Client — the transport used by ServiceRegistrar
  • Authentication — auth for NATS™ subscribers
  • API Reference — ServiceRegistrar and payload types
Last modified on October 28, 2026
NATS™ ClientSSE Standard
On this page
  • Lifecycle events
    • Gateway restart re-registration
  • Usage
  • Nav declaration — validated at enrollment
  • Health checks
  • Configuration options
  • ServiceRegistryPort
  • Next steps
TypeScript
TypeScript
TypeScript