Fetch API: współczesny interfejs do HTTP w przeglądarce
fetch() nie zmienił modelu komunikacji wprowadzonego przez AJAX.
Nadal chodzi o:
JavaScript -> HTTP request -> backend -> response -> JavaScript
Zmienił się interfejs.
Najprostszy request
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.
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.
const response = await fetch("/missing");
Jeżeli serwer poprawnie odpowie:
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:
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
await fetch("/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "Alice"
})
});
Browser tworzy request składający się z:
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
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:
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.
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:
cancel waiting != rollback server work
Fetch nie projektuje API
Po fetch() nadal pozostaje pytanie:
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.