Lab 10: HTTP Requests & Fetch API

Objective

Make HTTP requests in Node.js using the built-in fetch API (Node 18+), handle JSON responses, work with headers and query parameters, and implement basic error handling for network failures.

Background

Every modern application communicates over HTTP — fetching data from APIs, submitting forms, or calling microservices. Node.js 18 introduced a native fetch API identical to the browser's, so the same skills work everywhere. Understanding HTTP methods, status codes, headers, and JSON parsing is foundational for any backend or full-stack developer.

Time

45 minutes

Prerequisites

  • Lab 09 (Node.js File System) or equivalent

  • Basic understanding of async/await (Lab 06)

Tools

  • Node.js 20 LTS

  • Docker image: innozverse-js:latest


Lab Instructions

Step 1: Your First Fetch Request

The fetch API returns a Promise that resolves to a Response object. You must call .json() (also a Promise) to parse the body.

💡 Why two awaits? fetch resolves when the headers arrive. The body streams separately — .json() reads and parses the full body. This lets you inspect status codes before consuming the (potentially large) body.

📸 Verified Output:


Step 2: Query Parameters & Multiple Resources

Build URLs with query strings to filter API responses.

💡 URLSearchParams automatically encodes special characters, handles arrays, and produces a properly formatted query string. Always use it instead of manual string concatenation to avoid injection bugs.

📸 Verified Output:


Step 3: POST Requests — Sending Data

Use POST to create resources. Set the method, headers, and body.

💡 Status 201 vs 200: REST APIs use 201 Created for successful POST requests. Always JSON.stringify() your request body and set Content-Type: application/json so the server knows how to parse it.

📸 Verified Output:


Step 4: PUT and DELETE — Updating & Removing

💡 PUT vs PATCH: PUT replaces the entire resource (you must send all fields). PATCH updates only the provided fields. Most REST APIs support both.

📸 Verified Output:


Step 5: Error Handling — Network vs HTTP Errors

fetch only throws on network errors (DNS failure, timeout). HTTP 404/500 still resolve — you must check response.ok.

💡 The response.ok trap is one of the most common bugs in JavaScript. await fetch(badUrl) doesn't throw on 404 — it resolves with response.ok === false. Always check it before calling .json().

📸 Verified Output:


Step 6: Request Timeout with AbortController

Node.js fetch has no built-in timeout — use AbortController.

💡 AbortController is a Web API standard — it works in browsers, Node.js, and Deno. The signal propagates the abort to any number of concurrent requests. Always clear your timeout to avoid memory leaks.

📸 Verified Output:


Step 7: Parallel Requests with Promise.all

Fetch multiple resources concurrently instead of waiting one-by-one.

💡 Promise.all fires all requests simultaneously and waits for all to complete. Sequential await waits for each before starting the next — 3× slower for independent requests. Use Promise.allSettled when you want results even if some fail.

📸 Verified Output:


Step 8: Build a Mini API Client Class

Combine everything into a reusable HTTP client.

💡 Encapsulating HTTP logic in a class gives you a single place to add auth tokens, logging, retry logic, and base URL management. Real-world API clients (Axios, got, ky) use the same pattern.

📸 Verified Output:


Verification

Expected: User, posts count, and created post ID all print without errors.

Common Mistakes

Mistake
Fix

Not checking response.ok

Always check before calling .json()

Forgetting JSON.stringify(body)

The body must be a string, not an object

Missing Content-Type header on POST

Server won't parse the body correctly

Sequential awaits for independent requests

Use Promise.all for concurrency

No timeout on fetch

Use AbortController with setTimeout

Summary

You can now make all four HTTP methods (GET, POST, PUT, DELETE), handle both network and HTTP errors, add timeouts, run requests in parallel, and wrap it all in a clean API client class. These patterns form the backbone of every Node.js backend and CLI tool.

Further Reading

Last updated