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

© 2026 PrimeBrick. MIT License. v3.11.2

github
Backend
Frontend
Microservices
    Developer Guide
      Microservices OverviewArchitectureConventionsAdding a Service
      Services
powered by Zudoku
Developer Guide

Adding a Service

Each microservice in this repo is self-contained: own package.json, own DB schema, own createMicroservice boot. This is the checklist every new service follows — nothing more, nothing less.

1. Package

Code
{ "name": "primebrick-<code>", "private": true, "capabilities": ["<domain_capability>"], "scripts": { "dev": "bun --hot src/index.ts", "build": "tsc", "db:migrate": "bun scripts/database-patch-apply.ts", "generate:docs": "bun scripts/build-docs-pack.ts" }, "dependencies": { "@primebrick/dal-pg": "workspace:*", "@primebrick/sdk": "workspace:*" } }
  • capabilities[] is what ServiceRegistrar publishes — the BE registry and the AI docs-pack pull both read it.
  • version:auto/prebuild call ../scripts/version-sync.mjs (root script) to keep versions aligned across services.

2. Boot — src/index.ts

One createMicroservice(options) call. Required options:

serviceName, envSchema (closed-set: exactly DATABASE_URL + DATABASE_SCHEMA — assertDbEnv() parses DATABASE_SCHEMA into the string[] domain; [0] is the service's owned schema and is sent to the BE as the service.register schema claim), initDal/dalClose, configRepositoryAdapter, authConfigPort, healthCheckPort, routeHandler (factory-produced only).

Domain wiring goes in onReady(ctx) — NATS subscribers, config holder init, cron jobs. See SDK's microservice-anatomy for the full 12-stage boot sequence.

3. Database — db-meta/

Code
db-meta/ patches/ # ordered, sha256-registered migrations (dal-pg runMigrations, at boot) MANIFEST.json # Ed25519-signed patch_name → content_sha256 map keys.json # trusted signing keys (dev/release)

Seed at minimum: http_port, service_base_url, service_client_shield_secret (type secret + _header companion), nats_url, plus the service's own config keys — all via a numbered patch.

4. Routes + Spec — src/service-routes.ts

Declare the service's whole HTTP surface ONCE via makeServiceRoutes — it derives BOTH the request routers and /api/v1/openapi.json from the same declaration, so the spec can never drift from the registered routes:

Code
export const myServiceRouteHandler = makeServiceRoutes({ info: { title: "... Catalog", version: detectPackageIdentity().version ?? "0.0.0" }, serverUrl: "http://localhost:30XX", tags: [...], entities: [ /* EntityRouteDecl — CRUD router + spec ops derived from `permissions`/`meta`/`duplicate`; `spec:{tag,label,listResponse}` is doc cosmetics only, never a second op list */ ], rpc: [ MY_ROUTES /* RpcRoute[] — per-route `openapi` metadata feeds the spec; `{ routes, deps }` form for router deps (e.g. apiKeyPort) */ ], });

Pass the result straight to createMicroservice({ routeHandler }) — it is factory-branded. The handler carries .spec (the built OpenAPI doc): scripts/build-docs-pack.ts embeds myServiceRouteHandler.spec. A route that exists but isn't documented (or vice versa) is impossible by construction — never hand-compose buildOpenApiSpec/entityCrudSpec/ makeOpenApiHandler separately.

6. Docs pack — scripts/build-docs-pack.ts

Copy the standard script verbatim, changing repo, docsDir (omit for dev-only services) and openapi. Build it in the deploy chain — ServiceRegistrar auto-advertises docs_pack when dist/docs-pack/ manifest.json exists.

7. Registration — automatic

On boot the service publishes service.register; the BE registry learns name, base_url, capabilities, endpoints — and the /ws/<code>/* proxy path becomes reachable. Nav/sidebar entries for its screens are declared by the service itself in the endpoints.nav block of the service.register payload (route_prefixes + nav[] + full routes census — validated at enrollment; the BE's static module-nav-meta registry is reserved for BE-owned modules like settings).

Root level: the service declares partOf in its registration options (service_registry.part_of). Its single FE route namespace is derived, never free-chosen: /{part_of}/{code} — e.g. partOf: "system" + code job → all nav hrefs and census routes under /system/job/**. When the service IS its own root (partOf === code, e.g. crm) the namespace collapses to /{code} → /crm/**. A mismatch is rejected at register time.

Display name: declare labelKey (→ service_registry.label_key) — the i18n key for the module's localized name, resolved against the service's OWN *.translations schema (seed it there, like nav[].label_key entries). Every UI surface (app launcher, breadcrumbs, module menus) renders it via resolveModuleLabel with the raw name as fallback.

8. Docs

<svc>/docs/dev-guide/index.mdx — always. <svc>/docs/user-guide/ only if the feature is end-user visible. Update docs/dev-guide/overview.mdx's service catalog.

Last modified on October 28, 2026
ConventionsDocs Knowledge Base
On this page
  • 1. Package
  • 2. Boot — src/index.ts
  • 3. Database — db-meta/
  • 4. Routes + Spec — src/service-routes.ts
  • 6. Docs pack — scripts/build-docs-pack.ts
  • 7. Registration — automatic
  • 8. Docs
JSON
TypeScript