Fetch in browsers
| GET JSON | |
| POST forms | |
| Check response.ok | |
| Abort slow ones |
GET JSON
fetch takes a URL and returns a promise of the response. Await it, then await json for the parsed body. The compiler cannot know the body shape, so declare an interface and pin the parsed value to it:
interface User { id: number; name: string; }
async function loadUsers(): Promise<User[]> {
const res = await fetch("https://api.example.com/users");
const users = (await res.json()) as User[];
return users;
}
Two awaits in a row: one for the headers, one for the body. The function returns a promise of users, and the caller awaits that too.
POST forms
POST sends data instead of asking for it. Pass the method and the body in an options object, with headers that name the format. JSON.stringify turns the object into text the server can read:
async function saveUser(name: string): Promise<void> {
await fetch("https://api.example.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name }),
});
}
The Content-Type header tells the server that JSON is coming. Without it, many backends refuse to parse the body at all.
Check response.ok
fetch only rejects on network failure. A missing page still resolves, so check the ok flag before touching the body. Throw a plain error with the status code, and the caller handles every failure in one catch:
async function loadUsers(): Promise<User[]> {
const res = await fetch("https://api.example.com/users");
if (!res.ok) {
throw new Error("request failed: " + res.status);
}
return (await res.json()) as User[];
}
ok is true for statuses 200 through 299. Anything else lands in the throw, and res.status tells exactly which one.
Abort slow ones
Slow requests should not hang the page. An AbortController hands its signal to fetch and cancels on demand: the timer below aborts after five seconds, and the awaited fetch rejects at once. The snippet assumes a module script, where top-level await is legal:
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 5000);
try {
await fetch("https://api.example.com/users", { signal: ctrl.signal });
} finally {
clearTimeout(timer);
}
The timer fires abort unless the fetch finishes first. finally clears the timer either way, so no stray timeouts survive the request.
Next: Forms
Article author: Arthur Isaev