Skip to content
OpenPerf
Esc
↑↓navigate↵open⌘Jpreview
On this page

Nest apps

Rules for NestJS apps, currently apps/api.

Layout

  • src/features/<names>/: one feature each, named in the plural. Files: <name>.module.ts, .controller.ts, .service.ts, an optional .repository.ts, dto/, response/, libs/, index.ts.
  • src/common/<module>/: code that two or more features use. Import it through its barrel; a module over five files splits into role folders with their own barrels.
  • Filenames are kebab-case with a Nest role suffix: .service.ts, .guard.ts, .dto.ts.
  • Queries shared with other apps live in @openperf/server; their feature has no repository.

Requests and responses

  • Schemas are zod: dto/<name>.dto.ts exports <name>Dto, response/<name>.response.ts exports <name>Response. Types come from z.infer.
  • Declare both sides on the handler: @Query({ schema }) or @Body({ schema }), @ApiResult(schema), and @ApiErrors() on the controller.
  • Body and response schemas get .meta({ id }); query schemas don’t. Response schemas have no .transform(); time-based defaults belong in the request.
  • Errors carry a stable code from buildApiError; GlobalExceptionFilter builds the response.

Access

  • Every route declares @Access("read" | "write" | "authenticated") or @Public(true), on the controller and per handler where it differs. src/app.module.test.ts checks every route.
  • API tokens reach only @AllowApiToken(true) routes, and read tokens can’t write. Read the caller with @Actor().

Code

  • Only entry points call readEnvironment(). Providers inject ENVIRONMENT, functions take values as parameters, tests use createTestEnvironment().
  • Slow or retried work goes to worker-jobs through JobQueueService. Pass the transaction when the job must commit with a row change.
  • Never import type a class used for dependency injection.
  • Route handlers and migrations are exempt from verb-first names. Prefer Bun APIs.

Was this page helpful?