What Actually Happens When You Call an API?
What Actually Happens When You Call an API?
You write one line of code:
const response = await fetch('/api/orders');It looks simple.
But behind that single line is a surprisingly long journey. Your request may pass through DNS, networking, TLS, a load balancer, authentication, application logic, a database, external services, serialization, and finally make its way back to your browser.
Understanding this journey changes the way you debug problems. Instead of saying, “The API is slow,” you start asking, “Which part of the request lifecycle is slow?” That is a much better engineering question.
The big picture
At a high level, the journey looks like this:
Not every API follows exactly this architecture. A local development environment may be much simpler. A large production system may contain API gateways, CDNs, caches, message queues, several services, and multiple databases.
The fundamental idea remains the same: your API call is a conversation between multiple systems.
1. Your application creates the request
Let's start with a common frontend request:
const response = await fetch('/api/orders');The browser constructs an HTTP request. Conceptually, it might look like:
GET /api/orders HTTP/1.1
Host: example.com
Accept: application/json
Authorization: Bearer <token>The HTTP method describes the operation. The URL identifies the resource. Headers provide additional information. Authentication information may identify the user. A request can also contain a body, especially for operations such as creating or updating data.
For example:
POST /api/orders
Content-Type: application/json
Authorization: Bearer <token>
{
"product_id": 42,
"quantity": 2
}At this point, the browser has created the request. It still needs to find where to send it.
2. DNS finds the server
Suppose your frontend calls:
https://api.example.com/ordersYour computer needs to know where api.example.com lives. DNS, the Domain Name System, translates the hostname into an IP address.
api.example.com
↓
DNS lookup
↓
IP addressThe actual process can involve browser, operating system, network resolver, and authoritative DNS caches. The point is that the friendly domain name needs to become a network destination.
Why does this matter? Because DNS can be part of your performance story. If DNS resolution is slow, the application can feel slow even when the backend is healthy.
3. A network connection is established
Once the client knows the destination, the request travels through the network. For HTTPS, TLS establishes an encrypted channel between the client and server.
Conceptually:
Client
↓
Network connection
↓
TLS handshake
↓
Encrypted HTTP communicationModern deployments can use HTTP/1.1, HTTP/2, or HTTP/3. Their details differ, but the important engineering point is the same: there is a network underneath the API call, and networks introduce latency and failure possibilities.
One API call can also trigger several more network calls behind the scenes when the backend talks to other services.
4. The request may reach a load balancer first
In a simple application, a request may go directly to one application server. Production systems often look more like this:
Client
↓
Load Balancer
↓
Application Server 1
Application Server 2
Application Server 3A load balancer distributes traffic across application instances. This provides additional capacity and can improve resilience.
This works especially well when application instances are stateless. Any suitable instance can handle a request without depending on memory stored inside another instance.
Request A → Server 1
Request B → Server 3
Request C → Server 2Shared state can instead live in systems designed for it, such as a database or cache, when appropriate.
5. Authentication happens
Once the application receives the request, it may need to determine who is making it.
For example:
Authorization: Bearer eyJ...The backend can validate the credentials, identify the user, and check permissions.
Authentication asks:
Who are you?
Authorization asks:
Are you allowed to do this?
A user can be authenticated successfully and still lack permission to access an administrator endpoint.
This distinction is useful when debugging responses such as 401 Unauthorized and 403 Forbidden.
6. The application executes business logic
Assume authentication succeeds. The backend now performs the actual application work.
For an endpoint such as:
GET /api/ordersThe flow might be:
Receive request
↓
Identify user
↓
Check permissions
↓
Determine account
↓
Load orders
↓
Apply business rules
↓
Transform data
↓
Build responseThis is where your application's behavior lives.
For example, if customers can only see their own orders, the backend should apply that rule when querying data. An API endpoint is not merely a database wrapper. It represents business behavior.
7. The database may become involved
Many API requests eventually need data from a database.
For example:
SELECT id, total, status, created_at
FROM orders
WHERE user_id = $1
ORDER BY created_at DESC;The database now has its own work to perform. It parses the query, chooses an execution plan, reads the required data, performs joins or sorting when needed, handles locks and transactions, and returns the result.
This is why database performance is often API performance.
If an endpoint takes 800 milliseconds and the database query takes 700 milliseconds, optimizing the frontend does not solve the fundamental problem.
You need to investigate the database access pattern, query plan, indexes, locks, connection pool, and resource pressure.
8. The backend may call other services
The database is not necessarily the end of the journey.
A backend might communicate with payment providers, notification systems, search services, AI services, or other internal services.
For example:
Client
↓
Order API
├── Database
├── Payment Provider
└── Notification ServiceNow one user action can involve several systems.
This creates real distributed systems questions. What happens if the payment provider takes five seconds? What happens if the email provider is unavailable? What happens if your database succeeds but an external service fails? What happens if the request times out even though the external provider completed the operation?
These are production engineering problems, not theoretical edge cases.
9. The backend creates the response
Eventually, the application has enough information to return a response.
For example:
{
"orders": [
{
"id": 101,
"total": 2500,
"status": "paid"
}
]
}The application serializes its internal representation into JSON or another response format.
Conceptually:
HTTP/1.1 200 OK
Content-Type: application/json
{
"orders": [...]
}The response then travels back through the network.
10. The response reaches your frontend
Finally:
const data = await response.json();Your application now has the data. It can update state, render UI, and give the user the result.
From the user's perspective, the entire journey may have looked instantaneous. Behind that experience, many systems may have participated.
Where can an API become slow?
Suppose a user says, “The application feels slow.” Do not immediately optimize random code.
Break the request into stages:
DNS
↓
Connection
↓
Server processing
↓
Database
↓
External services
↓
Response
↓
Browser processingThen measure each important stage.
For example:
DNS 20 ms
Connection 40 ms
Application 80 ms
Database 650 ms
External API 50 ms
Response 30 msNow the problem is clearer. The database is consuming 650 milliseconds. That gives you a direction for investigation.
You can ask whether the query is missing an index, returning too much data, using an inefficient join, waiting on a lock, or receiving an unexpected execution plan.
The better debugging sequence is simple:
Measure first. Find the dominant cost. Understand why. Fix the bottleneck. Measure again.
What about caching?
Sometimes the best database query is the query you never execute.
If an endpoint returns information that changes rarely, a cache can avoid repeated database work.
Request
↓
Application
↓
Cache
↓
ResponseIf the data is already cached, the database may not need to be contacted.
Caching can dramatically improve performance, but it introduces its own tradeoffs around expiration, invalidation, stale data, and cache availability.
Good engineering is not about adding technologies. It is about understanding the problem each technology solves.
What changes when traffic increases?
Imagine an application growing from 100 requests per minute to 10,000 and eventually 1,000,000.
The architecture may need to evolve.
You might introduce application replicas, load balancing, caching, database indexes, read capacity, queues, background workers, CDNs, rate limiting, partitioning, or service isolation.
But none of these should be added simply because they sound scalable.
The right question is:
What is currently limiting the system?
If the database is the bottleneck, adding five more application servers may not help. If an external provider is slow, increasing database capacity will not solve it. If one workload is overwhelming the system, isolating that workload may be the better boundary.
Why observability matters
You cannot reliably improve a system you cannot see.
For production APIs, I want visibility into request rate, response time, error rate, database latency, external API latency, CPU and memory usage, queue depth, background job duration, and cache hit rate.
The goal is not to collect metrics because metrics are fashionable. The goal is to answer questions.
Why did the API become slower today?
Which endpoint is responsible?
Did database latency increase?
Did traffic increase?
Did one customer generate unusual activity?
Did an external dependency become slower?
Good observability turns debugging from guessing into investigation.
A useful mental model for debugging
When an API is slow, think in terms of a timeline:
Request starts
│
├── DNS
│
├── Connection
│
├── Server processing
│
├── Database
│
├── External service
│
└── Response
↓
ClientThe question is not immediately, “How can I make it faster?”
First ask:
Where did the time go?
Then understand why that stage is expensive.
Then decide whether optimization is worth the added complexity.
The deeper lesson
An API is often introduced as a simple concept: send a request and receive a response.
Real software engineering is about what happens between those two events.
There is networking. There is security. There is infrastructure. There is application architecture. There is business logic. There are databases. There are external dependencies. There are failure modes. There are performance constraints. There are scaling decisions.
Once you understand the lifecycle, debugging becomes easier. Architecture discussions become more meaningful. Performance problems become easier to reason about. You start seeing software as a system rather than a collection of functions.
Final takeaway
The next time you write:
fetch('/api/orders');remember that you are not simply calling a function.
You are initiating a conversation between multiple systems.
The request needs to find its destination. A secure connection needs to be established. Infrastructure may route it. Authentication may validate it. Business logic may process it. A database may retrieve the required information. External services may participate. The response then has to make its way back to the client.
And all of this needs to happen reliably, securely, and fast enough for the user.
That is the real engineering behind a simple API call.
The better you understand the journey, the better you become at designing, debugging, and scaling software.
What should you learn next?
The natural next step is understanding what happens inside the database when your API executes a query: query planning, indexes, joins, transactions, locks, connection pools, and database performance.
Because once you understand the API request lifecycle, the next question becomes:
What exactly happens when the application asks the database for data?
Newsletter
Get new posts in your inbox.
New posts, in your inbox. Nothing else goes to that list.