coding•TUTORIAL

Your API Is Slow. Your Database Might Not Be.

How to trace API latency properly instead of blaming the database for every slow request.

The Pathak
The Pathak@pathak
•
Sep 29, 2026
•
7 min read
Your API Is Slow. Your Database Might Not Be.
A user clicks a button. The spinner appears. Nothing happens for two seconds. Then the data finally arrives. The first thing many developers do is look at the database. "The query must be slow." Sometimes it is. But sometimes the database takes 40 milliseconds and the API takes 1.8 seconds. The database gets blamed simply because it is the easiest component to blame. A better approach is to stop guessing and measure the entire request.

Where Does the Time Actually Go?

A typical API request is not simply:
API → Database → Response
There can be several stages between the user clicking a button and seeing the result:
Browser
   ↓
Network
   ↓
Middleware
   ↓
Authentication
   ↓
Controller
   ↓
Business Logic
   ↓
Database Query
   ↓
Data Processing
   ↓
Serialization
   ↓
Response
   ↓
Browser
Every one of those steps can introduce latency. If you only measure the database, you are measuring one piece of the request and assuming it represents everything else.

Let's Build a Slow Endpoint

Imagine a simple endpoint that returns posts:
app.get("/api/posts", async (req, res) => {
  const posts = await prisma.post.findMany({
    orderBy: {
      createdAt: "desc"
    }
  });

  res.json(posts);
});
The endpoint works. The database query works. The response looks correct. But users complain that the page feels slow. So where is the problem? Instead of changing the query immediately, measure it.

First: Measure the Database

const dbStart = performance.now();

const posts = await prisma.post.findMany({
  orderBy: {
    createdAt: "desc"
  }
});

const dbEnd = performance.now();

console.log(`Database: ${(dbEnd - dbStart).toFixed(2)}ms`);
Suppose the result is:
Database: 47.21ms
That doesn't look particularly alarming. But your API is still taking 1.4 seconds. That immediately changes the investigation. The database is probably not the main bottleneck.

Measure the Entire Request

Now measure from the beginning of the request to the response.
app.get("/api/posts", async (req, res) => {
  const requestStart = performance.now();

  const posts = await prisma.post.findMany({
    orderBy: {
      createdAt: "desc"
    }
  });

  const requestEnd = performance.now();

  console.log({
    databaseAndRequestTime: `${(requestEnd - requestStart).toFixed(2)}ms`
  });

  res.json(posts);
});
But this still isn't enough. You need to break the request into meaningful stages.

Measure the Individual Stages

For example:
const start = performance.now();

const authStart = performance.now();
const user = await authenticateUser(req);
const authEnd = performance.now();

const dbStart = performance.now();
const posts = await getPosts();
const dbEnd = performance.now();

const serializationStart = performance.now();
const response = posts.map(formatPost);
const serializationEnd = performance.now();

console.log({
  authentication: `${(authEnd - authStart).toFixed(2)}ms`,
  database: `${(dbEnd - dbStart).toFixed(2)}ms`,
  serialization: `${(serializationEnd - serializationStart).toFixed(2)}ms`,
  total: `${(performance.now() - start).toFixed(2)}ms`
});
Now you might discover:
authentication: 35ms
database: 52ms
serialization: 890ms
total: 1,012ms
That changes everything. Your database wasn't slow. Your API was doing too much work after the query.

The N+1 Problem Can Hide in Plain Sight

One of the classic causes of API slowness is the N+1 query problem. Imagine retrieving 100 posts. You run one query to get the posts:
const posts = await prisma.post.findMany();
Then you fetch the author separately for every post:
for (const post of posts) {
  post.author = await prisma.user.findUnique({
    where: {
      id: post.authorId
    }
  });
}
You wanted one list. You ended up making one query for the list and another query for every item. That is roughly:
1 + 100 = 101 database queries
Even if every individual query is fast, the combined request can become painfully slow. Depending on your ORM and data model, the solution might involve selecting related data in a single query, batching requests, or restructuring how the data is loaded.

Don't Fetch Everything Just Because You Can

Another common problem is returning far more data than the frontend needs. Suppose your post table contains:
id
title
content
excerpt
coverImage
author
metadata
analytics
createdAt
updatedAt
draftData
internalNotes
But the homepage only needs:
id
title
excerpt
coverImage
createdAt
There is little reason to transfer the entire record. With Prisma, for example:
const posts = await prisma.post.findMany({
  select: {
    id: true,
    title: true,
    excerpt: true,
    coverImage: true,
    createdAt: true
  }
});
Smaller responses mean less database work, less serialization, less network transfer and less work for the browser.

Indexes Matter — But Don't Add Them Blindly

Suppose you frequently run:
SELECT *
FROM posts
WHERE section = 'Coding & Walkthroughs'
ORDER BY created_at DESC;
If the table becomes large, the database may need to examine a substantial amount of data before producing the result. An appropriate index can make a significant difference. But this is where measurement matters again. Don't create ten indexes because someone told you indexes make databases faster. Indexes have costs. They consume storage and can make writes more expensive because the database also has to maintain those indexes. The right question isn't: "Can I add an index?" It is: "Does the query planner show that this query would benefit from one?"

Use EXPLAIN Instead of Guessing

PostgreSQL gives you tools for examining how a query is executed. For example:
EXPLAIN ANALYZE
SELECT *
FROM posts
WHERE section = 'Coding & Walkthroughs'
ORDER BY created_at DESC;
This can reveal whether PostgreSQL is using an index, scanning the table, sorting a large result, or spending most of its time somewhere else. Now you're optimizing based on evidence rather than intuition.

Sometimes the Network Is the Problem

Imagine this:
Database:        40ms
Business logic:  20ms
Serialization:   15ms
Network:        700ms
Your database query is excellent. Your API logic is fine. The user still waits almost a second. This can happen when services are deployed far apart, when requests travel through several network layers, or when the response is unnecessarily large. The lesson is simple: Application performance is bigger than database performance.

And Then There Is Caching

Suppose your homepage requests the same list of published posts thousands of times. If that data doesn't change every second, querying the database for every request may be unnecessary. A cache can change the flow:
Request
   ↓
Cache
   ↓
Found?
  / \
Yes  No
 ↓    ↓
Return Database
       ↓
     Cache
       ↓
     Return
But caching isn't automatically a performance solution. It introduces another problem: How do you know when the cached data is stale? You now have to think about expiration, invalidation, consistency and cache misses. A cache can make a fast system faster. It can also make a complicated system much harder to reason about.

Measure Before You Optimize

This is probably the most useful habit in the entire process. When an API is slow, don't immediately:
Add an index
Add Redis
Rewrite the query
Change the ORM
Increase server resources
Rewrite the endpoint
First find out where the time is going. A useful investigation might look like:
1. Measure total request time
2. Measure database time
3. Measure external API calls
4. Measure business logic
5. Measure serialization
6. Inspect database query plans
7. Check response size
8. Check network latency
9. Identify the largest contributor
10. Optimize that component
11. Measure again
That final step matters. If you change something and don't measure again, you don't actually know whether your optimization worked.

The Slow API Is a Symptom

A request taking 1.5 seconds tells you that something is slow. It doesn't tell you what. The database might be responsible. Or authentication. Or an external API. Or a loop processing thousands of objects. Or serialization. Or network latency. Or simply sending far too much data to the browser. The mistake is treating the symptom as the diagnosis.

The Rule I Keep Coming Back To

Performance optimization becomes much easier when you stop asking: "What should I optimize?" and start asking: "Where is the time actually going?" A slow API isn't a database problem until you've measured the database and found that it is. Measure first. Find the bottleneck. Change one thing. Measure again. That's a much better performance strategy than rewriting half your backend because one request felt slow.
#API#Backend#Database#NodeJs#PostgreSQL#REST API#web dev#debugging#optimization
The Pathak
The Pathak@pathak

Lead Software Architect, Writer & Thinker exploring Technology, Science, Code, Ideas, and Words.

1

Discussion (1)

Type @ to tag readers

Sign in to join the discussion, reply, and tag authors.

Sep 29, 2026
Very insightful and well explained

More from CODING

The LeetCode Era Is Changing: What Happens When AI Enters the Coding Interview?
CODING•ARTICLE

The LeetCode Era Is Changing: What Happens When AI Enters the Coding Interview?

For years, aspiring software engineers have spent months solving algorithmic problems to prepare for technical interviews. Now, Google is experimenting with AI-assisted code comprehension, while HackerRank is questioning the interview culture it helped popularize. The change raises a bigger question: when AI can generate code, should hiring focus less on writing it and more on understanding, debugging, and validating it?

The Pathak1 min read
HTTP Just Got a New Method: Meet QUERY
CODING•ARTICLE

HTTP Just Got a New Method: Meet QUERY

HTTP has a new method: QUERY. It is designed for complex, read-only queries that don't fit comfortably inside a URL, allowing structured query data to be sent in the request body while retaining safe and idempotent semantics.

The Pathak6 min read
What Actually Happens When You Click “Login”?
CODING•TUTORIAL

What Actually Happens When You Click “Login”?

A login button looks simple, but behind that click is a chain of validation, HTTP requests, database queries, password verification, authentication and protected resources. Let's follow the entire journey.

The Pathak5 min read