DEV Community

Abhishek tiwari
Abhishek tiwari

Posted on

Silent N+1 Database Query Bottlenecks in TypeScript & Node.js REST APIs

Most slow PostgreSQL queries in Node.js REST APIs don’t happen because of missing database indexes.

They happen because of silent N+1 query patterns executing inside map loops.

When building REST endpoints with Node.js and TypeScript, it’s easy to write clean-looking code like this:

const users = await getUsers();
const posts = await Promise.all(
users.map(user => getPostsByUserId(user.id))
);

On a local machine with 10 test records? It runs in 5ms.
In production with 1,000 active users? That single HTTP request triggers 1,001 database roundtrips—exhausting your PostgreSQL connection pool and bringing response times to a crawl.

Here is how we eliminate N+1 query bottlenecks in Node.js + Postgres services:

  1. Batching with WHERE id = ANY($1)
    Instead of querying per item in a loop, collect IDs into a single array and fetch all matching records in one database roundtrip.

  2. Leveraging PostgreSQL JOIN and JSON_AGG
    Let Postgres do the heavy lifting. Using SQL joins with json_agg() yields structured JSON directly from the database layer, skipping Node.js mapping overhead entirely.

  3. DataLoader Pattern for Nested REST Dependencies
    If you have nested dependencies across sub-services, leverage DataLoader to automatically batch and cache queries per request context.

  4. Query Count Logging in Staging
    Enable query logging or APM tracing in non-production environments to catch high query counts before code hits production.

Clean code is great, but database network roundtrips are expensive.

How do you catch N+1 query patterns during code reviews in your team? Let’s discuss below! 👇

Top comments (0)