Skip to main content

Adding an API endpoint

  1. Add or extend a controller in src/modules/<area>/<feature>/. Do not put a new domain controller in src/common unless it is cross-cutting auth or files, which is where those already live.
  2. Set the path without the /v1 prefix. Versioning adds it. Use VERSION_NEUTRAL only when you mean to, as health and voice webhooks do.
  3. Declare a DTO class with class-validator. The global ValidationPipe uses transform, whitelist, and forbidNonWhitelisted.
  4. Add @ApiOperation and @DocResponse so the generated OpenAPI document picks the route up. Do not also hand-write the fields into this docs site.
  5. Add @TenantScoped() for tenant data.
  6. Add @RequirePermissions('AREA:ACTION') unless every authenticated user should be allowed. There is no later deny.
  7. Add @AuditLog if the call mutates and should be audited. The listener writes in-process.
  8. Inject a service, not DatabaseService.
  9. If the write has a side effect, enqueue the outbox inside the service transaction. Do not emit from the controller.
  10. Add a Jest spec under test/ and run yarn verify:test-pairs.

Public routes (@PublicRoute) skip the access JWT. Refresh token is the pattern that is public and still has its own guard. Copy that only when you have a second credential to check.

Warehouse-sensitive routes still pass WarehouseGuard for logged-in users. You do not need a second warehouse check if the id is in the header, route, query, or body. The guard already narrows.

See Authenticated request.