1---2name: node-patterns3description: Node/TypeScript Backend Patterns4---56# Node/TypeScript Backend Patterns78## Project layout910- Feature folders over layer folders: `src/orders/{orders.router,orders.service,orders.repo,orders.test}.ts` beats `src/{routes,services,repos}/orders.ts` scattered three places.11- One composition root (`src/app.ts`) wires everything; entry (`src/main.ts`) only boots. Handlers stay thin — parse/validate → service call → shape response.1213## Async discipline1415- No floating promises: every promise is awaited, returned, or explicitly `void`-ed with a comment. Unhandled rejection = crash in modern Node.16- Propagate `AbortSignal` through request-scoped work (fetch, DB timeouts); cancel on client disconnect for expensive handlers.17- No sync I/O (`readFileSync`, `execSync`) on request paths — boot time only.18- Concurrency with intent: `Promise.all` for independent work, `for…of await` when order matters, a limiter (`p-limit`) when fan-out is unbounded.1920## Config2122- Typed and validated at boot (zod or equivalent): missing/invalid env kills the process at startup with a clear message — never `process.env.X!` scattered through the codebase.23- One `config.ts` exports the parsed object; nothing else reads `process.env`.2425## Errors2627- Central error taxonomy: `AppError` subtypes with status + code (`NotFound`, `Validation`, `Conflict`, `Upstream`). Handlers throw domain errors; ONE error middleware maps them to responses (problem+json shape).28- Never `catch (e) {}` — swallow nothing; wrap-and-rethrow with context or let it propagate.2930## DI boundaries3132- NestJS: providers with explicit scopes; beware request-scoped bleeding into singletons.33- Express/Fastify: manual factories — services take dependencies as constructor/args, never import singletons directly. Makes the testing shape below possible.3435## ESM/CJS pitfalls3637- Pick ONE module system per package and align `"type"`, `tsconfig` `module`, and tooling. Mixed default/named interop errors are config bugs, not code bugs.38- `__dirname` does not exist in ESM — `import.meta.url` + `fileURLToPath`.3940## Testing shape4142- vitest/jest for units (services with faked repos), supertest against the composed app for HTTP behavior (status, body, headers — not internals).43- Test files co-located with the feature. Integration tests own their fixtures.4445## Logging4647- Structured (pino), request-correlated (request-id middleware); never `console.log` in request paths. Log the error object, not `err.message` alone (stack + cause matter).