From fetch() to the First Byte
- date
- category
- Frontend
- also in
- Networking · Observability
- reading
- 2 min / 313 words
We have this code:
const response = await fetch(
"https://api.example.com/users/42"
);
What happens between that line and receiving a Response?
There is no single identical path for every request, but we can build the right model.
1. Fetch creates an abstract request
The application defines:
URL
method
headers
body
credentials mode
cache mode
redirect mode
signal
The browser validates the request against Fetch and the security model.
JavaScript does not create raw HTTP bytes.
2. The browser checks policies and local state
Depending on the request, these can come into play:
HTTP cache
cookies
CORS
service worker
redirect policy
For example a cross-origin request may require a preflight.
Which means that:
one fetch()
can mean:
zero origin requests -> cache hit
one request -> normal network fetch
multiple requests -> preflight / redirects
3. The browser needs a suitable connection
A connection fit for reuse may already exist.
If not:
DNS resolution
|
v
transport setup
|
v
TLS / secure session setup
|
v
HTTP protocol negotiation
The exact stages depend on the HTTP version and the existing connection state.
4. The request becomes a wire representation
Semantically:
GET /users/42
Accept: ...
Cookie: ...
but how it is sent depends on the version.
HTTP/1.1
-> textual message framing
HTTP/2
-> binary frames on an HTTP/2 stream
HTTP/3
-> HTTP frames on QUIC streams
The application should not feel that difference functionally.
5. The backend starts answering
The response headers come first.
In DevTools, part of the time up to that moment is usually associated with TTFB:
request start
|
v
network + server work
|
v
first response byte
So TTFB is not simply:
backend execution time
It can also include network and connection setup costs.
6. fetch() can give a Response before the whole body arrives
const response = await fetch(url);
Then:
const data = await response.json();
Those are two different stages.
With a large or streaming response body the difference can be significant.
The model:
headers available
|
v
Response object
|
v
body continues arriving
|
v
body consumed
7. DevTools shows the effect of many layers
The Network panel can show:
Queueing
Stalled
DNS
Initial connection
SSL
Request sent
Waiting / TTFB
Content download
Not every request pays every cost.
If the connection is reused:
DNS/TCP/TLS cost ≈ already paid
If the response comes from cache, the path can be even shorter.
If the request is HTTP/2 or HTTP/3, many requests can share the lower layers of the connection.
The whole model
JavaScript
|
v
Fetch API
|
v
browser policy + cache + cookies
|
v
HTTP semantics
|
v
HTTP/1.1 | HTTP/2 | HTTP/3
|
v
TCP | QUIC
|
v
network
|
v
backend
|
v
response headers
|
v
Response
|
v
response body
That is the most important outcome of this series.
When the frontend is slow, the question:
"is the API slow?"
is usually too general.
Better questions are:
Did the request go to the network at all?
Did it wait for a connection?
Was it preceded by a preflight?
Are the requests serialized by our own code?
Which protocol was used?
When did the headers arrive?
How long did the body take to download?
Only with that model does the Network panel become a diagnostic tool rather than a list of endpoints.