Common mistakes in Node.js, NestJS and PostgreSQL backend code Intermediate
The problem
Section titled “The problem”Most production bugs don’t come from hard algorithms. They come from a few dozen mistakes that repeat over and over: a forgotten await, money as floats, a transaction running on the wrong connection, fetch without a timeout.
This cluster gathers those mistakes into a catalog. Each one is a BAD/GOOD pair with a “Why”: what breaks, when it blows up, and why. All behavior was tested on Node 24 and PostgreSQL 17.
Where bugs live in a request
Section titled “Where bugs live in a request”Loading diagram…
- Wrongly typed input goes straight to the DB or hangs a regex.
- Broken async sends errors to the wrong
try/catch, or crashes the process. - Resources are the last stage but affect every other request in turn.
In this cluster
Section titled “In this cluster”| Page | Question it answers | Example mistakes |
|---|---|---|
| Async & errors | Why aren’t errors caught, why does the process exit on its own? | async in forEach; return without await inside try; fire-and-forget without .catch |
| Numbers, dates, strings | Why is money off, dates off, ids changing value? | count(*) returns "1"; new Date('2026-10-18') is UTC; timestamp columns without a timezone |
| Database & transactions | Why are queries slow, data duplicated or updates lost? | N+1; check-then-insert; BEGIN via pool.query |
| API & security | Why does the API leak data or get abused? | string concatenation into raw SQL; fetching objects by id without an ownership check; jwt.decode instead of verify |
| Performance & resources | Why does it run fine in testing but get slow/OOM in production? | pbkdf2Sync in a handler; Promise.all over 10k items; fetch without a timeout |
If you’re new, read in the table’s order. If you’re experienced, jump straight to the checklist at the end of each page.
Using the catalog in code review
Section titled “Using the catalog in code review”Loading diagram…
- Comment with a link: “This is return without await”. The recipient sees the full example and consequences; the reviewer doesn’t re-explain.
- Assign severity: mistakes that corrupt data or crash the process (fire-and-forget, float money, check-then-insert) are blockers; performance issues on rarely called paths may be nits.
- Turn it into a lint rule/test when it recurs often: e.g.,
@typescript-eslint/no-floating-promisescatches unhandled Promises. - New mistakes found in postmortems get added as a BAD/GOOD pair on the right page.
For the full review process, see Code review.
Related articles
Section titled “Related articles”| Topic | Article |
|---|---|
| The event loop, why synchronous code blocks every request | Node.js event loop |
| Doing transactions correctly with ORMs | Transaction patterns |
| Input validation, secrets, injection | Secure coding |
| Timeouts and retries for outbound calls | Timeout budget |
Next: Async & errors.