Node.js backend patterns: layered architecture, TypeScript, validation, error handling, security, deployment. Use when building REST APIs, Express/Fastify/Hono/NestJS servers, or server-side TypeScript.
From compound-engineeringnpx claudepluginhub iliaal/compound-engineering-plugin --plugin compound-engineeringThis skill uses the workspace's default tool permissions.
references/api-design.mdreferences/database-production.mdreferences/security.mdreferences/typescript-config.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Executes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
Verify before implementing: For framework-specific APIs (Express 5, Fastify 5, Node.js 22+ built-ins), search current docs via search_docs before writing code. Training data may lag current releases.
| Context | Choose | Why |
|---|---|---|
| Edge/Serverless | Hono | Zero-dep, fastest cold starts |
| Performance API | Fastify | 2-3x faster than Express, built-in schema validation |
| Enterprise/team | NestJS | DI, decorators, structured conventions |
| Legacy/ecosystem | Express | Most middleware, widest adoption |
Ask user: deployment target, cold start needs, team experience, existing codebase.
src/
├── routes/ # HTTP: parse request, call service, format response
├── middleware/ # Auth, validation, rate limiting, logging
├── services/ # Business logic (no HTTP types)
├── repositories/ # Data access only (queries, ORM)
├── config/ # Env, DB pool, constants
└── types/ # Shared TypeScript interfaces
import type { } for type-only imports -- eliminates runtime overheadinterface for object shapes (2-5x faster type resolution than intersections)unknown over any -- forces explicit narrowingz.infer<typeof Schema> as single source of truth -- never duplicate types and schemasas assertions -- use type guards insteaddeclare module 'pkg' { const v: unknown; export default v; } in types/ambient.d.tsZod (TypeScript inference) or TypeBox (Fastify native). Validate at boundaries only: request entry, before DB ops, env vars at startup. Use .extend(), .pick(), .omit(), .partial(), .merge() for DRY schemas.
Custom error hierarchy: AppError(message, statusCode, isOperational) → ValidationError(400), NotFoundError(404), UnauthorizedError(401), ForbiddenError(403), ConflictError(409)
Centralized handler middleware:
AppError → return { error: message } with statusCodeconst asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);Codes: 400 bad input | 401 no auth | 403 no permission | 404 missing | 409 conflict | 422 business rule | 429 rate limited | 500 server fault
Contract-first: define route schemas (Zod schemas, Fastify JSON Schema, or OpenAPI spec) before writing handler logic. The schema is the contract -- implementation follows. Generate OpenAPI/Swagger docs from these schemas for interactive API documentation.
{ error: { code, message, details? } } structure. Centralize in the error handler middleware. Callers build error handling once; inconsistent errors force per-endpoint special cases..parse() on request body/params, Fastify schema validation). Services and repositories trust that input was validated at entry -- no redundant checks scattered through business logic./users), max 2 nesting levels (/users/:id/orders)/api/v1/{ data, pagination?: { page, limit, total, totalPages } }?page=1&limit=20&status=active&sort=createdAt,descLocation header on 201. Use 204 for successful DELETE with no body.| Pattern | Use When |
|---|---|
async/await | Sequential operations |
Promise.all | Parallel independent ops |
Promise.allSettled | Parallel, some may fail |
Promise.race | Timeout or first-wins |
Never use readFileSync or other sync methods in production -- use fs.promises or stream equivalents. Offload CPU work to worker threads (Piscina). Stream large payloads.
@fastify/under-pressure (or equivalent) -- monitor event loop delay, heap, RSS; return 503 when thresholds exceeded.fast-json-stringify for 2-3x faster serialization.opossum for outbound service calls. States: CLOSED (normal) -> OPEN (failing, return fallback) -> HALF_OPEN (probe). Prevents cascade failures when downstream services are down.as any, non-null assertions on untrusted data, // @ts-ignore), treat it as a design smell and find the typed solutiontsc --noEmit && npm test pass with zero warnings before declaring donetsc --noEmit passes with zero errorsnpm test passes with zero failuresas any, @ts-ignore) in new code