JavaScript: Fetch API and HTTP Requests
Learn how to communicate with servers using the Fetch API to send and receive data over HTTP.
What is the Fetch API?
The Fetch API is the modern, built-in browser interface for making HTTP requests. It replaces the older XMLHttpRequest with a cleaner, Promise-based design that works naturally with async/await.
Fetch API
fetch() is available in all modern browsers and Node.js 18+ without any import.
- Returns a Promise that resolves to a
Responseobject - The Promise rejects only on network failure (no internet, DNS error): NOT on 4xx/5xx HTTP errors
- Works with async/await for clean, readable async code
- Supports all HTTP methods: GET, POST, PUT, PATCH, DELETE
First Fetch Call
fetch() returns a Promise: chain .then() to parse the body and read the data.
Press Run to execute the code and see output here.
Making GET Requests
GET is the default HTTP method for fetch(). Pass a URL and you are done: no options object needed.
GET Request
GET requests retrieve data from a server without modifying anything.
- No request body: data is passed via the URL or query parameters
- Check
res.ok(true for 200–299) before parsing: a 404 still resolves the Promise - Use
res.json()to parse a JSON response body - Responses are cached by the browser by default for GET requests
GET Request with Error Check
Always check res.ok: a 404 resolves the Promise but is still an error.
Press Run to execute the code and see output here.
Making POST Requests
POST requests send data to the server to create a resource. Pass the method, headers, and a serialized body in the options object.
POST Request
Always set Content-Type: application/json and JSON.stringify the body when sending JSON.
- Set
method: "POST"in the options object - Set
Content-Type: application/jsonso the server knows how to parse the body - Serialize the payload with
JSON.stringify()before passing it asbody - The server usually responds with the created resource and a 201 status
POST Request
Send a new post to the server: the API echoes it back with an assigned id.
Press Run to execute the code and see output here.
Request Headers
Headers carry metadata about the request: the content format, authentication tokens, and more. Pass them as an object or a Headers instance in the options.
Headers
Headers tell the server what format you are sending and what format you expect back.
Content-Type: format of the request body (e.g.application/json)Accept: format you want in the response (e.g.application/json)Authorization: authentication token, e.g.Bearer <token>- Use the
Headersconstructor for dynamic or repeated header manipulation
Custom Request Headers
Send auth tokens and format hints alongside the request.
Press Run to execute the code and see output here.
Response Object
The Response object returned byfetch() contains the status, headers, and the unread body stream.
Response Properties
Inspect the Response before reading the body to decide how to handle it.
res.ok: true if status is 200–299res.status: the HTTP status code (200, 404, 500…)res.statusText: human-readable status ("OK", "Not Found")res.headers: a Headers object: read withres.headers.get("content-type")- The body can only be read once: calling
res.json()consumes it
Inspecting the Response Object
Read status and headers before consuming the body.
Press Run to execute the code and see output here.
Response Methods: json(), text(), blob()
The response body is a stream. Call one of these methods to read and convert it into a usable format. Each returns a Promise.
Body Reading Methods
Choose the method that matches the content type of the response.
res.json(): parses the body as JSON: use for REST APIsres.text(): reads the body as a plain string: use for HTML or plain textres.blob(): reads as a binary Blob: use for images, PDFs, filesres.arrayBuffer(): reads as raw binary data: use for audio, video processing- You can only call one of these once per response
json() vs text()
Pick the reader that matches the server's response format.
Press Run to execute the code and see output here.
Handling Fetch Errors
Fetch errors fall into two categories: network failures (Promise rejects) and HTTP errors (Promise resolves but res.ok is false). You must handle both.
Two Error Categories
fetch() only rejects on network failure: a 404 or 500 is a resolved Promise with res.ok = false.
- Network error: no connection, DNS failure: Promise rejects, caught by
catch - HTTP error: 4xx or 5xx: Promise resolves, check
res.okand throw manually - A helper wrapper that throws on HTTP errors keeps your call-site code clean
- Always have a
catchortry...catch: unhandled rejections crash Node.js
Safe Fetch Wrapper
Centralise the ok check in a wrapper so every caller gets consistent error handling.
Press Run to execute the code and see output here.
Request Options
The second argument to fetch() is an options object that controls the method, headers, body, caching, credentials, and more.
fetch() Options Object
The options object gives you full control over every aspect of the HTTP request.
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"headers: object or Headers instancebody: string, FormData, Blob, or URLSearchParamscredentials: "omit" | "same-origin" | "include" (for cookies)cache: "default" | "no-store" | "reload" | "no-cache"
PATCH Request with Full Options
Partially update a post by sending only the changed fields.
Press Run to execute the code and see output here.
CORS Basics
CORS (Cross-Origin Resource Sharing) is a browser security policy that blocks requests to a different origin (domain, port, or protocol) unless the server explicitly allows it.
CORS
CORS errors are enforced by the browser: the request reaches the server, but the browser blocks the response.
- A CORS error means the server did not send the
Access-Control-Allow-Originheader - You cannot fix CORS from the browser/client: the server must opt in
- During development, use a proxy (Next.js API routes, Vite proxy, or a CORS proxy)
credentials: "include"sends cookies cross-origin but requiresAccess-Control-Allow-Credentials: trueon the server
CORS in Practice
Public APIs allow all origins: private servers must explicitly opt in.
Press Run to execute the code and see output here.
FormData API
FormData lets you send form fields and file uploads as multipart/form-data: the same encoding browsers use for native HTML form submissions.
FormData
Use FormData when uploading files or submitting forms with mixed text and binary data.
- Pass FormData as the
bodydirectly: do NOT set Content-Type manually; the browser sets it with the correct boundary - Append fields with
formData.append(name, value) - Append files with
formData.append("file", fileInput.files[0]) - You can also build FormData from an existing
<form>element:new FormData(formElement)
FormData Submission
Append fields to FormData and pass it directly as the body: no JSON.stringify needed.
Press Run to execute the code and see output here.
Query Parameters
Query parameters are appended to the URL to filter or paginate data. Use URLSearchParams to build them safely: it handles encoding automatically.
URLSearchParams
URLSearchParams encodes special characters and builds clean query strings without manual string concatenation.
- Construct with an object:
new URLSearchParams({ page: 1, limit: 10 }) - Convert to string with
.toString():page=1&limit=10 - Append to the URL:
`${baseUrl}?${params}` - Handles encoding: spaces become
%20, ampersands are escaped automatically
Query Parameters with URLSearchParams
Build the query string safely and append it to the base URL.
Press Run to execute the code and see output here.
REST API Integration
A real-world API client wraps all the individual HTTP methods (GET, POST, PUT, DELETE) into a reusable module with shared base URL, headers, and error handling.
REST API Client Pattern
Centralise fetch configuration in a small API module so call-site code stays clean.
- Define a base URL and default headers once
- Expose named functions:
getPost(id),createPost(data),deletePost(id) - Handle errors centrally in one place rather than at every call site
- This pattern scales easily: swap the base URL for staging vs production via an env variable
Minimal REST API Client
A small request helper and an api object give you a clean interface to any REST endpoint.
Press Run to execute the code and see output here.
Knowledge Check
1. What does fetch() return?
2. Why must you call res.json() after a successful fetch?
3. fetch() rejects its Promise when the server returns a 404 or 500 status. True or false?
4. Which fetch option sets the HTTP method to POST?
5. What header must you set when sending JSON in a POST request body?
6. What does CORS stand for and what does it control?
7. How do you append query parameters to a fetch URL cleanly?