APIs, Data Access and Service Architecture
Production Node.js services succeed or fail on the boring parts: clear layers, validated config, correctly used connection pools, short transactions, idempotent jobs, sound auth and a shutdown that drains before it exits.
Key points
- 1
Layer the code: routes and controllers handle HTTP, services hold business rules, repositories own data access, so logic can be reused from jobs and tested with fakes.
- 2
Validate
process.envat startup (values are always strings, so coerce numbers) and fail fast on bad config. - 3
Use
pool.query()for single statements; for transactions, check out one client, run BEGIN to COMMIT or ROLLBACK on it, andrelease()it infinally. Inside ORM transactions, always use thetxobject. - 4
Total connections = instances × pool size. Size pools with Little's Law (rate × hold time) and put a pooler in front of Postgres for serverless or many instances.
- 5
Queues give at-least-once delivery: make jobs idempotent, pass idempotency keys to external systems, and claim keys atomically with a unique constraint.
- 6
Verify JWTs (never just decode them), keep access tokens short-lived, rotate refresh tokens with reuse detection, and prefer httpOnly cookies on the web.
- 7
Shut down from the outside in: fail readiness, stop accepting, drain in-flight requests, close pools and queues, then exit within the grace period.
Common traps
An unreleased pool client on an error path slowly exhausts the pool, and with the default
connectionTimeoutMillis: 0, requests then wait forever.In-process cron libraries run once per replica; schedule outside the replicas or deduplicate through the queue.
Without
pool.on('error'), an error on an idle client during a database failover crashes the whole process.
Read the source
Test yourself on APIs, Data Access and Service Architecture
Ten questions, with the answer and explanation after each one.