Topic 11.1
HTTP for JavaScript Developers
In one line
HTTP is the request-and-reply conversation between the browser and a server: every request has a method, a URL and headers, every reply has a status code, headers and a body, and the browser's CORS rules decide which replies your JavaScript may read.
Think of it like this
Ordering at a restaurant counter. You hand over an order slip (the request): what you want to do ("order", "cancel"), which item, and a few notes ("no onions", "I'm paying by card"). The kitchen hands back a tray with a ticket on it (the response): the ticket says how it went ("here you go", "sold out", "kitchen on fire"), a few notes, and then the food itself. HTTP is exactly this slip-and-tray conversation between your browser and a server.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Client and server
- The client (your browser) asks for things; the server (a computer on the internet) answers.
- Request
- What the client sends: a method, a URL, headers and sometimes a body.
- Response
- What the server sends back: a status code, headers and usually a body.
- Method
- The verb of a request: GET to read, POST to create or send, PUT/PATCH to change, DELETE to remove.
- Status code
- A three-digit number in the response that says how it went, like 200 (fine) or 404 (not found).
- Header
- A
name: valuenote attached to a request or response, likeContent-Type: application/json. - JSON
- A text format for data that looks like JavaScript objects and arrays. Almost every web API uses it.
- Origin
- The scheme, host and port of a URL together, like
https://hectal.in. Two URLs with any part different are different origins. - CORS
- Cross-Origin Resource Sharing: headers a server sends to let pages from other origins read its replies.
- Idempotent
- Doing it twice has the same effect as doing it once. GET, PUT and DELETE are idempotent; POST usually isn't.
Step by step
01Look at a real request
Open DevTools (F12), go to the Network tab and reload any page. Every row is one request. Click a row: the Headers panel shows the method, URL, status code and both sets of headers; Response shows the body.
Written out as plain text, a small HTTP/1.1 request and its reply look like this. (HTTP/2 and HTTP/3 send the same information in a compact binary form, but DevTools shows it the same way.)
GET /api/users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=60
{"id":42,"name":"Asha"}02One request, one response
The client always speaks first, and each request gets exactly one response. The server never "pushes" a plain HTTP response by itself; for that there are other tools (WebSockets, server-sent events) built on top.
03Read the status code first
Your code should decide what to do from the number: 2xx means use the body, 401 means log in again, 404 means show "not found", 429 means slow down and retry later, 5xx means the server is having trouble (a retry may help).
A 4xx or 5xx reply is still a reply: the server answered. That difference matters a lot in the next topic, where fetch treats a 500 as a successful conversation with a sad answer.
04Send and receive JSON
To send data, turn it into JSON text and say so with a header. To receive it, read the body as text and parse it (fetch's response.json() does both steps for you).
const user = { name: "Asha", joined: new Date("2024-01-15T10:30:00Z"), nickname: undefined };
const text = JSON.stringify(user);
console.log(text); // {"name":"Asha","joined":"2024-01-15T10:30:00.000Z"}
const back = JSON.parse(text);
console.log(typeof back.joined); // string, not a Date05Cross-origin requests and the preflight
When https://hectal.in calls https://api.other.com with JSON, the browser first sends an OPTIONS request listing the method and headers it wants to use. Only if the server answers with matching Access-Control-Allow-* headers does the real request go out, and only if the real reply also allows the origin can your code read it.
06When CORS says no
If the server doesn't send the right headers, the request may still have reached the server, but the browser hides the reply from your code. You see a red CORS message in the console and fetch rejects with TypeError: Failed to fetch. Your JavaScript can't read why, on purpose: that's what stops a random site from reading your bank's pages with your cookies.
The fix is always on the server (or a proxy on your own origin that calls the API for you). mode: "no-cors" doesn't help: it gives you an opaque response whose body you can't read.
Try it yourself
- 1
Add a route to the fake server
In the first example, add a branch for
PATCH /api/users/1that replies200 OKwith{ id: 1, name: "Asha K" }, then callshow("PATCH", "/api/users/1"). Predict the line before you press Run. - 2
Find the edges of ok
In the first example's console,
okwastruefor 200, 201 and 204. Predictokfor 299, 300 and 199, then check:console.log(new Response(null, { status: 299 }).ok). (Hint: the constructor only allows 200 to 599, so 199 throws a RangeError.) - 3
Watch a real preflight
On any site that calls an API on another domain, open DevTools → Network, tick Preserve log, and filter by Fetch/XHR. Look for an
OPTIONSrow right before aPOSTorPUTto the same URL, and read itsAccess-Control-*response headers.
Code & diagrams
`Response` is the browser's real response class. `ok` is `true` for any status from 200 to 299. A 204 reply has no body, so there is nothing to parse.
Preview
Console
Nothing logged yet.Expected console output
GET /api/users/1 -> 200 OK (ok: true)
body: { id: 1, name: 'Asha' }
POST /api/users -> 201 Created (ok: true)
body: { id: 2 }
DELETE /api/users/2 -> 204 No Content (ok: true)
GET /api/users/999 -> 404 Not Found (ok: false)
body: { error: 'Not found' }Preview
Console
Nothing logged yet.Expected console output
200 success: use the body
201 success: use the body
204 success: use the body
304 redirect or cached: the browser usually handles it
400 client error: fix the request, don't retry as-is
401 client error: log in again
404 client error: fix the request, don't retry as-is
429 client error: too many requests, wait and retry
500 server error: a retry later may work
503 server error: a retry later may workThe `Headers` object lowercases names. In JSON, the date became a string, `note` vanished, and the `Set` became an empty object.
Preview
Console
Nothing logged yet.Expected console output
application/json
application/json
true
[ [ 'content-type', 'application/json' ], [ 'x-request-id', 'abc123' ] ]
{"item":"Tea","placedAt":"2024-01-15T10:30:00.000Z","tags":{}}
string false {}// Page on https://hectal.in calling a server that sends no CORS headers
try {
const res = await fetch("https://api.other-site.example/data");
} catch (err) {
console.log(err.name, err.message); // TypeError Failed to fetch
}
// The console also shows (from the browser, not from your code):
// Access to fetch at 'https://api.other-site.example/data' from origin 'https://hectal.in'
// has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
// on the requested resource.
// The fix lives on the server. Example reply headers that allow it:
// Access-Control-Allow-Origin: https://hectal.in
// Access-Control-Allow-Methods: GET, POST, PUT
// Access-Control-Allow-Headers: Content-Type
// Vary: OriginBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Parse an HTML error page as JSON
In the first example, make the 404 reply send "<!DOCTYPE html><html><body>Not found</body></html>" as its body instead of the JSON, and keep await res.json().
Break #2
Try to fix CORS from the front end
Add headers: { "Access-Control-Allow-Origin": "*" } to your own fetch call to an API on another origin that doesn't allow you.
Myth vs fact
Myth
A 404 or 500 means the request failed to reach the server.
Fact
Any status code means the server received the request and answered. A network failure (offline, DNS, CORS block) produces no status at all.
Myth
CORS protects the server from other websites.
Fact
CORS protects users: it stops a page on one origin reading responses from another with the user's cookies. The server still receives the request; tools like curl ignore CORS entirely. Real server protection is authentication and CSRF defences.
Myth
POST is more secure than GET.
Fact
Both travel inside the same HTTPS encryption. The difference is that GET parameters end up in the URL, which lands in history, logs and Referer headers, so sensitive data belongs in a body, not a query string.
Myth
JSON is just JavaScript.
Fact
JSON is a stricter text format: keys must be double-quoted, no trailing commas, no comments, no undefined, functions or dates. JSON.parse throws on anything else.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
A request counts as simple (no preflight) only with method
GET,HEADorPOST, no custom headers, and aContent-Typeoftext/plain,multipart/form-dataorapplication/x-www-form-urlencoded.application/jsonalways preflights. Servers can cache the preflight answer withAccess-Control-Max-Ageto save a round trip. - ▸
Cookies are only sent cross-origin when the request uses
credentials: "include"and the server replies withAccess-Control-Allow-Credentials: trueand a specific origin (not*). Cookies also needSameSite=None; Secureto travel on cross-site requests at all. - ▸
A server that varies
Access-Control-Allow-Originby the request'sOriginmust also sendVary: Origin, or a shared cache (CDN) may serve one origin's permission to another. See/system-designfor caching and CDNs. - ▸
HTTP/2 and HTTP/3 multiplex many requests over one connection, so the old trick of bundling everything into one request matters less than it did with HTTP/1.1, where browsers opened only about six connections per host.
Remember this
- 1
HTTP (HyperText Transfer Protocol) is the set of rules browsers and servers use to talk. The browser (the client) sends a request; the server (a computer somewhere that holds the data) sends back one response. Every web page, image, script and data call travels this way. HTTPS is the same protocol inside an encrypted connection (TLS), so nobody in between can read or change it. Modern sites use HTTPS for everything, and many browser APIs only work on HTTPS pages.
- 2
A request has four parts. The method says what you want to do:
GETreads something,POSTcreates something or sends data to be processed,PUTreplaces something,PATCHchanges part of something,DELETEremoves something. The URL says which thing (https://api.example.com/users/42). Headers arename: valuenotes about the request (Accept: application/jsonmeans "please answer in JSON"). The body carries data, usually only withPOST,PUTandPATCH.GETis meant to be safe (it changes nothing), andGET,PUTandDELETEare idempotent: sending the same request twice has the same effect as sending it once, which is why browsers and libraries may retry them. - 3
A response starts with a status code, a three-digit number grouped by its first digit. 2xx means success (
200 OK,201 Created,204 No Content, which has no body). 3xx means "look elsewhere" (301 Moved Permanently,304 Not Modified, which tells the browser its cached copy is still good). 4xx means the client made a mistake (400 Bad Request,401 Unauthorizedmeaning "who are you?",403 Forbiddenmeaning "I know who you are, and no",404 Not Found,409 Conflict,422 Unprocessable Content,429 Too Many Requests). 5xx means the server failed (500 Internal Server Error,502 Bad Gateway,503 Service Unavailable). The words after the number are the status text; code should check the number, not the text. - 4
Response headers describe the body and how to treat it:
Content-Type: application/jsonsays the body is JSON,Cache-Control: max-age=60says it may be reused for 60 seconds,Set-Cookiestores a cookie. Header names are case-insensitive:Content-Typeandcontent-typeare the same header. In JavaScript, theHeadersobject handles that for you and always gives names back in lowercase. - 5
Most APIs (Application Programming Interfaces: URLs built for programs rather than people) send JSON (JavaScript Object Notation): text that looks like a JavaScript object, with double-quoted keys and only strings, numbers, booleans,
null, arrays and objects.JSON.stringify(value)turns a value into JSON text to send;JSON.parse(text)turns received text back into a value. Dates,undefined, functions,MapandSetdon't survive the trip: a date becomes a string, andundefinedproperties disappear. - 6
The browser enforces the same-origin policy. An origin is the scheme, host and port together:
https://hectal.inandhttps://api.hectal.inare different origins, and so arehttp://andhttps://versions of one site. JavaScript on one origin may send a request to another, but it may only read the reply if the server allows it with CORS (Cross-Origin Resource Sharing) headers such asAccess-Control-Allow-Origin: https://hectal.in. For "non-simple" requests (aPUT, or aPOSTwithContent-Type: application/json, or custom headers) the browser first sends anOPTIONSpreflight request to ask permission. CORS is set on the server; nothing in your front-end code can switch it off.
Explain it without notes
What are the parts of an HTTP request and of an HTTP response?
Explain the status code families and give two examples of each.
What is the same-origin policy, and how does CORS relax it?
When does the browser send a preflight request, and why?
Practice
Write a function isRetryable(status) that returns true for 408, 429 and any 5xx status, and false otherwise. Log the result for 404, 429 and 503.
Build a Headers object with Accept: application/json and Authorization: Bearer abc, then log every header name, one per line.
Trade-offs
- ↔
JSON is readable and universal but larger and slower to parse than binary formats; for huge payloads, compression (gzip, Brotli) on the server usually matters more than switching format.
- ↔
Calling a third-party API straight from the browser is simple but needs CORS and exposes any key you send; calling it through your own server hides keys and avoids CORS, at the cost of running that server.
Done when you can
I can name the parts of a request and a response.
I can say what 200, 201, 204, 304, 400, 401, 403, 404, 429, 500 and 503 mean.
I know header names are case-insensitive and JSON loses dates,
undefinedand functions.I can explain what an origin is and why CORS exists.
I can tell when a request needs a preflight.