An Express.js API service in TypeScript, Node 20+, deployed as a Docker container.
- Language: TypeScript 5.x, strict mode on
- Framework: Express 4.x with async route handlers
- Validation:
zodfor request body validation - DB:
pg(node-postgres) with raw SQL or a thin query builder - Logging:
pinofor structured JSON logs - Package manager: pnpm
pnpm install
pnpm dev # tsx watch
pnpm build # tsc
pnpm start # node dist/server.js
pnpm test # vitest
pnpm lint # eslint
pnpm typecheck # tsc --noEmit- Formatter: Prettier
- Linter: ESLint with
@typescript-eslint - Async/await everywhere; never raw promises
- Always return explicit response from handlers (
return res.json(...)) - Validate request bodies with
zodschemas before touching them - Comments: only when the why is non-obvious
- Do not use callback-style APIs — use the promise variants (e.g.,
fs/promises) - Do not put business logic in route handlers — extract to
services/ - Do not throw plain strings — throw typed
Errorsubclasses caught by error middleware - Do not use
any— useunknownand narrow with zod - Do not call
process.exitoutside of startup error paths
src/server.ts— app setup, middleware chain, error handlersrc/routes/— route handlers (one file per resource)src/services/— business logic, side-effect-free where possiblesrc/db.ts— connection poolsrc/schemas/— zod validation schemassrc/config.ts— env-driven configtsconfig.json— strict mode required