Konrad Kowalski (rootsher)Principal Platform & Reliability Architect110001111111100100000101001011111100100011110101

Od fetch() do pierwszego bajtu

data
kategoria
Frontend
także w
Networking · Observability
czytanie
1 min / 256 słów

Mamy kod:

js
const response = await fetch(
  "https://api.example.com/users/42"
);

Co dzieje się pomiędzy tą linią a otrzymaniem Response?

Nie istnieje jedna identyczna ścieżka dla każdego requestu, ale możemy zbudować właściwy model.

1. Fetch tworzy request abstrakcyjny

Aplikacja definiuje:

text
URL
method
headers
body
credentials mode
cache mode
redirect mode
signal

Browser waliduje request zgodnie z Fetch i security model.

JavaScript nie tworzy raw HTTP bytes.

2. Browser sprawdza polityki i lokalny stan

Zależnie od requestu mogą wejść:

text
HTTP cache
cookies
CORS
service worker
redirect policy

Przykładowo request cross-origin może wymagać preflight.

To oznacza, że:

text
one fetch()

może oznaczać:

text
zero origin requests   -> cache hit
one request            -> normal network fetch
multiple requests      -> preflight / redirects

3. Browser potrzebuje odpowiedniego connection

Może już istnieć connection nadające się do reuse.

Jeżeli nie:

text
DNS resolution
  |
  v
transport setup
  |
  v
TLS / secure session setup
  |
  v
HTTP protocol negotiation

Dokładne etapy zależą od HTTP version i istniejącego connection state.

4. Request staje się wire representation

Semantycznie:

text
GET /users/42
Accept: ...
Cookie: ...

ale sposób przesłania zależy od wersji.

text
HTTP/1.1
-> textual message framing

HTTP/2
-> binary frames on an HTTP/2 stream

HTTP/3
-> HTTP frames on QUIC streams

Aplikacja nie powinna odczuwać tej różnicy funkcjonalnie.

5. Backend zaczyna odpowiadać

Najpierw pojawiają się response headers.

W DevTools część czasu do tego momentu zwykle kojarzymy z TTFB:

text
request start
  |
  v
network + server work
  |
  v
first response byte

TTFB nie jest więc po prostu:

text
backend execution time

Może zawierać również koszty sieci i connection setup.

6. fetch() może dać Response, zanim pobierzemy całe body

js
const response = await fetch(url);

Następnie:

js
const data = await response.json();

To dwa różne etapy.

Przy dużym lub streamingowym response body różnica może być znacząca.

Model:

text
headers available
  |
  v
Response object
  |
  v
body continues arriving
  |
  v
body consumed

7. DevTools pokazuje efekt wielu warstw

Network panel może pokazać:

text
Queueing
Stalled
DNS
Initial connection
SSL
Request sent
Waiting / TTFB
Content download

Nie każdy request przechodzi przez każdy koszt.

Jeśli connection jest reuse'owane:

text
DNS/TCP/TLS cost ≈ already paid

Jeśli odpowiedź pochodzi z cache, ścieżka może być jeszcze krótsza.

Jeśli request jest HTTP/2 albo HTTP/3, wiele requestów może współdzielić niższe warstwy connection.

Cały model

text
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

To jest najważniejszy rezultat tej serii.

Kiedy frontend jest wolny, pytanie:

„czy API jest wolne?”

jest zwykle zbyt ogólne.

Lepsze pytania brzmią:

text
Czy request w ogóle wyszedł do sieci?
Czy czekał na connection?
Czy został poprzedzony preflightem?
Czy requesty są serializowane przez kod?
Jaki protokół został użyty?
Kiedy przyszły headers?
Jak długo pobierało się body?

Dopiero z takim modelem Network panel staje się narzędziem diagnostycznym, a nie tylko listą endpointów.