Node.js & TypeScript Development Standards¶
Target Runtime: Node.js 20 LTS+ | TypeScript 5.x
1. Language and Type Safety¶
- Strict mode:
"strict": trueintsconfig.json. No exceptions. - Type annotations: Required on all function parameters and return types. Avoid
any; useunknown+ type guards where the type is genuinely unknown. - Modern TypeScript: Use
satisfies,consttype parameters (TS 5.0+), and template literal types where they add precision. - Imports: Use ES module syntax (
import/export). Enable"module": "NodeNext"and"moduleResolution": "NodeNext". - No enum: Prefer
constobjects withas constor union types over TypeScriptenum.
2. Code Style & Quality¶
- Formatter:
Prettierwith default settings (or project.prettierrc). - Linter:
ESLintwith@typescript-eslintplugin. Enable@typescript-eslint/recommended-type-checked. - Naming:
camelCasefor variables/functions,PascalCasefor types/interfaces/classes,SCREAMING_SNAKE_CASEfor module-level constants. - Functions: Prefer named functions for top-level exports (easier stack traces). Arrow functions for callbacks and short lambdas.
- Error handling: Custom error classes extending
Error. Always includecausewhen re-throwing. Never swallow errors silently.
3. Async Patterns¶
- Use
async/awaitexclusively. No raw.then()chains in new code. - Use
Promise.all/Promise.allSettledfor concurrent independent async calls. - Validate all external inputs (HTTP requests, env vars) with
zodbefore use.
4. API Development (Express / Fastify / Hono)¶
- Route handlers must be typed: input via
zodschema, output via declared response type. - Separate concerns: router → controller (thin) → service (business logic) → repository (data access).
- HTTP status codes: use named constants from
http-status-codes. - Always return consistent error envelopes:
{ error: { code, message, details? } }.
5. Testing¶
- Framework:
vitest(preferred for ESM compatibility) orjestwithts-jest. - Structure: Co-locate tests (
*.test.ts) or use a__tests__/sibling directory. - Mocking: Use
vi.mock/jest.mock. Avoid testing implementation details; test behaviour. - Coverage: Aim for ≥80% branch coverage on service and utility layers.
6. Project Standards¶
- Config: Use environment variables validated via
zodat startup (src/config.ts). No hardcoded secrets. - Package manager:
npmwith lockfile committed (package-lock.json). Pin exact versions independencies; allow patch ranges indevDependencies. - Scripts:
package.jsonscripts are the entry points:build,dev,test,lint,typecheck.
7. Anti-Patterns to Avoid¶
require()in TypeScript (useimport).@ts-ignorewithout an explanatory comment.- Nested callbacks / callback hell.
- Storing secrets in source code or
.envfiles committed to git. - Unhandled promise rejections (use
process.on('unhandledRejection', ...)as a last resort, but prefer propertry/catch).
8. Enforcement¶
Configure tooling via root-level config files:
tsconfig.json:strict,noUncheckedIndexedAccess,exactOptionalPropertyTypeseslint.config.js:@typescript-eslint/recommended-type-checkedrulesetvitest.config.ts: coverage providerv8, threshold 80%.pre-commit-config.yaml(optional): runeslint,prettier --check,tsc --noEmiton staged files