Konrad Kowalski (rootsher)Principal Platform & Reliability Architect010101101100000000010100010000110111111000010100

Fetch API: współczesny interfejs do HTTP w przeglądarce

data
kategoria
Frontend
także w
Backend
czytanie
1 min / 227 słów

fetch() nie zmienił modelu komunikacji wprowadzonego przez AJAX.

Nadal chodzi o:

text
JavaScript -> HTTP request -> backend -> response -> JavaScript

Zmienił się interfejs.

Najprostszy request

js
const response = await fetch("/api/users/42");
const user = await response.json();

Pierwszy await czeka na odpowiedź HTTP.

Drugi konsumuje body.

To rozdzielenie jest ważne, ponieważ Response reprezentuje odpowiedź, a body może być strumieniem.

js
const response = await fetch(url);

response.status;
response.headers;
response.body;

response.body jest ReadableStream.

404 nie jest błędem fetch()

To jeden z częstszych błędów mentalnych.

js
const response = await fetch("/missing");

Jeżeli serwer poprawnie odpowie:

http
HTTP/1.1 404 Not Found

Promise zostanie spełniony.

fetch() zawodzi przede wszystkim wtedy, gdy nie uda się uzyskać odpowiedzi z powodu problemu transportowego lub polityki platformy.

Dlatego trzeba sprawdzać semantykę HTTP:

js
const response = await fetch(url);

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}

response.ok oznacza status z zakresu 200-299.

Request to coś więcej niż URL

js
await fetch("/api/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    name: "Alice"
  })
});

Browser tworzy request składający się z:

text
method
URL
headers
body
credentials
cache policy
redirect policy

fetch jest API do konstruowania i wykonywania requestów, nie funkcją „do pobierania JSON-a”.

Body jest konsumowalne

js
const response = await fetch(url);

await response.json();
await response.text(); // problem

Body nie jest dowolnie odczytywalnym polem. Jest strumieniem i po konsumpcji zostaje oznaczone jako użyte.

Jeżeli potrzebujemy dwóch konsumentów:

js
const copy = response.clone();

To wynika bezpośrednio ze streamingowego modelu Fetch API.

Cancellation

Request może przestać być potrzebny.

Przykład: użytkownik wpisuje kolejne znaki w wyszukiwarce.

js
const controller = new AbortController();

fetch("/search?q=rea", {
  signal: controller.signal
});

controller.abort();

Anulowanie nie oznacza cofnięcia operacji wykonanej już przez backend.

Oznacza przede wszystkim, że klient nie chce dalej czekać na wynik.

To ważna granica:

text
cancel waiting != rollback server work

Fetch nie projektuje API

Po fetch() nadal pozostaje pytanie:

js
fetch("/users/42")
fetch("/getUser?id=42")
fetch("/graphql")

Każdy z tych endpointów może używać HTTP poprawnie.

Różnią się jednak sposobem modelowania operacji i danych.

fetch odpowiada na pytanie:

jak wykonać request?

Nie odpowiada na:

jak powinno wyglądać nasze API?

Pierwszą popularną odpowiedzią na drugie pytanie jest REST.