Fetch API & HTTP Requests

📖 Concept

The Fetch API provides a modern, Promise-based way to make HTTP requests. It replaces the older XMLHttpRequest.

fetch(url, options) returns a Promise that resolves to a Response object.

Key points:

  • fetch only rejects on network errors, NOT on HTTP errors (4xx, 5xx)
  • You must check response.ok or response.status for HTTP errors
  • Response body can be read ONCE: .json(), .text(), .blob(), .arrayBuffer()

🏠 Real-world analogy: fetch is like sending a letter (request) and waiting for a reply (response). The postal service (network) delivers it. You might get a reply saying "denied" (404/500), but the delivery itself succeeded — that's why fetch doesn't reject on HTTP errors.

💻 Code Example

codeTap to expand ⛶
1// GET request
2async function getUsers() {
3 const response = await fetch("https://jsonplaceholder.typicode.com/users");
4 if (!response.ok) throw new Error(`HTTP ${response.status}`);
5 return response.json();
6}
7
8// POST request
9async function createUser(userData) {
10 const response = await fetch("https://api.example.com/users", {
11 method: "POST",
12 headers: {
13 "Content-Type": "application/json",
14 "Authorization": "Bearer token123"
15 },
16 body: JSON.stringify(userData)
17 });
18 if (!response.ok) {
19 const error = await response.json();
20 throw new Error(error.message);
21 }
22 return response.json();
23}
24
25// PUT, PATCH, DELETE
26async function updateUser(id, data) {
27 return fetch(`/api/users/${id}`, {
28 method: "PUT",
29 headers: { "Content-Type": "application/json" },
30 body: JSON.stringify(data)
31 });
32}
33
34async function deleteUser(id) {
35 return fetch(`/api/users/${id}`, { method: "DELETE" });
36}
37
38// Reusable API client
39class ApiClient {
40 constructor(baseURL, token) {
41 this.baseURL = baseURL;
42 this.token = token;
43 }
44 async request(endpoint, options = {}) {
45 const url = `${this.baseURL}${endpoint}`;
46 const res = await fetch(url, {
47 ...options,
48 headers: {
49 "Content-Type": "application/json",
50 "Authorization": `Bearer ${this.token}`,
51 ...options.headers
52 }
53 });
54 if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
55 return res.json();
56 }
57 get(endpoint) { return this.request(endpoint); }
58 post(endpoint, data) {
59 return this.request(endpoint, {
60 method: "POST", body: JSON.stringify(data)
61 });
62 }
63}
64
65// Abort a request
66const controller = new AbortController();
67setTimeout(() => controller.abort(), 5000); // Cancel after 5s
68try {
69 const res = await fetch("/api/data", { signal: controller.signal });
70} catch (err) {
71 if (err.name === "AbortError") console.log("Request cancelled");
72}

🏋️ Practice Exercise

Mini Exercise:

  1. Build a function that fetches data with timeout using AbortController
  2. Create an API wrapper class with get, post, put, delete methods
  3. Implement request caching — don't re-fetch if data was fetched within last 5 minutes
  4. Build a simple data fetching hook with loading, error, and data states

⚠️ Common Mistakes

  • fetch does NOT reject on HTTP errors (404, 500) — it only rejects on network failures. Always check response.ok!

  • Reading the response body twice — .json(), .text() etc. can only be called ONCE. Clone with response.clone() if needed

  • Forgetting Content-Type: application/json header for POST/PUT requests

  • Not handling the AbortError when using AbortController — it's a normal error that should be caught

  • Sending credentials (cookies) — fetch doesn't send cookies by default for cross-origin. Use credentials: 'include'

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Fetch API & HTTP Requests. Login to unlock this feature.