Last 30 Days
No notifications
fetch is the modern web API for HTTP — built into browsers and Node 18+. Returns a Promise, supports streams, headers, AbortController, and FormData. This page covers the everyday recipes (GET/POST/PUT/DELETE), error patterns (fetch only rejects on NETWORK failure — not on 404/500!), and how to add timeouts and cancellation.
# Fetch & HTTP Requests
const res = await fetch("/api/users");
const data = await res.json();| property | meaning |
res.ok | true if status is 200–299 |
res.status | numeric status (200, 404, 500…) |
res.statusText | "OK", "Not Found" … |
res.headers | Headers object — headers.get("content-type") |
Body methods (each can be called only once — body is a stream):
await res.json();
await res.text();
await res.blob();
await res.formData();
await res.arrayBuffer();fetch resolves. You MUST check res.ok yourself:
const res = await fetch("/api/users/999");
if (!res.ok) {
throw new Error(HTTP ${res.status} — ${res.statusText});
}
const user = await res.json();const res = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Alice", age: 25 }),
});method. DELETE usually has no body.const fd = new FormData();
fd.append("file", fileInput.files[0]);
fd.append("title", "Avatar");await fetch("/api/upload", { method: "POST", body: fd });
// ⚠️ DO NOT set Content-Type — the browser adds the multipart boundary
const params = new URLSearchParams({ q: "hello", page: 2 });
await fetch(/api/search?${params});await fetch("/api/secure", {
headers: {
"Authorization": Bearer ${token},
"Accept": "application/json",
},
});fetch("/api", { credentials: "include" }); // send cookies cross-origin
include requires the server to send proper CORS headers (Access-Control-Allow-Credentials: true and a specific Allow-Origin).async function fetchWithTimeout(url, ms = 5000, opts = {}) {
const ac = new AbortController();
const id = setTimeout(() => ac.abort(), ms);
try {
return await fetch(url, { ...opts, signal: ac.signal });
} finally {
clearTimeout(id);
}
}// modern shortcut (Node 17+/browsers)
fetch(url, { signal: AbortSignal.timeout(5000) });
async function api(path, { method = "GET", body, ...rest } = {}) {
const res = await fetch(/api${path}, {
method,
headers: { "Content-Type": "application/json", ...(rest.headers || {}) },
body: body && JSON.stringify(body),
...rest,
});
if (!res.ok) {
const text = await res.text();
throw new Error(API ${res.status}: ${text});
}
return res.status === 204 ? null : res.json();
}// usage
const user = await api("/users/1");
await api("/users", { method: "POST", body: { name: "Alice" } });
Access-Control-Allow-* headers. CORS is a *browser* enforcement — server-to-server fetches don't have it. mode: "no-cors" is almost never what you want; it returns an opaque response you can't read.const res = await fetch("/api/big");
const reader = res.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// value is a Uint8Array chunk
}