Conventions
API path conventions
All HTTP routes in microservices follow standardized path conventions so the BE's MCP Server can dispatch generic CRUD tools without per-entity configuration.
Entity CRUD (/api/v1/entities/:entity/...)
Database-backed entities with standard CRUD lifecycle use this pattern:
Code
Rules:
:entityis the snake_case plural noun (e.g.providers,config_entries). Never singular, never camelCase.:uuidis always the UUID path parameter.- Not all entities support all operations — unsupported operations simply don't register that route.
- The
metaendpoint returns the entity's field schema, consumed by the MCPget_entity_metatool and the FE for dynamic form generation.
Service actions (/api/v1/actions/:action)
Non-CRUD business actions:
Code
:actionis a snake_case verb-noun.- These are NOT exposed as MCP generic CRUD tools.
Webhooks (/webhook or /webhook/:identifier)
External callbacks (e.g. Brevo delivery events):
Code
- Webhooks use API key authentication, NOT JWT.
- Webhook paths do NOT use the
/api/v1/prefix.
System / Health
Code
OpenAPI spec requirements
Every microservice MUST export a complete OpenAPI 3.x spec at
GET /api/v1/openapi.json:
- List ALL implemented routes
- Use
operationIdin snake_case for every operation - Include
tagsgrouping operations by entity or category - Include
summaryanddescriptionfor every operation - Use
snake_casefield names in request/response schemas
Data model rules
Snake_case everywhere
DB columns, TS interfaces, JSON request bodies, and JSON response bodies
ALL use snake_case. A field named from_email in the DB is
from_email in the TS interface and from_email in the JSON response.
Never rename fields between layers.
Exception: External API adapters (e.g. Brevo expecting camelCase). The translation happens ONLY at the adapter boundary.
No DTO transformation
The DB row IS the TS model. Do not create intermediate DTO classes that
rename fields. Prefer spreading raw results (return { ...settings })
over field-by-field rebuilding.
No fake defaults on the read path
- Forbidden: lowercasing, uppercasing, trimming on the read path
- Forbidden: fallback string-literal defaults (
|| "...",?? "") for configuration data - A value either exists in the DB or it doesn't —
undefinedif missing,nullif the row exists but value is NULL,stringif present - Mandatory-field checks throw before the return, they don't fake a default
Type conversions are allowed
string(DB) →boolean(TS) via=== "true"— allowedstring(DB) →enum(TS) via validation + normalization — allowed
These are type conversions, not data-quality enforcement. Data quality is enforced at the write path (API upsert validation).
Package versioning
All package versions in package.json MUST be pinned to exact versions
(e.g. "typescript": "5.9.3"). NO ranges (^, ~, >=, *, latest)
are allowed for registry packages. This ensures every dev machine, CI
build, and production rebuild gets the exact same dependency tree.
Workspace dependencies use workspace:* (e.g.
"@primebrick/sdk": "workspace:*").
Next steps
- Architecture — NATS bus, BE proxy, SDK lifecycle
- EmailSender — Email sending microservice