---
title: Nest apps
description: 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](/self-hosting/services/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.
