AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 02 — HTTP, the Substrate of Web APIs

02 — HTTP, the Substrate of Web APIs

August 13, 20267 min read
Download as Markdown

"The thing fetch() uses" was how I used to file HTTP, until I traced one real request and saw the conversation underneath. The idea the whole web-API world rests on: HTTP is a line-by-line, stateless request/response conversation — method + path + headers + body in, status + headers + body back — and almost every convention we call "API design" is layered on top of that one exchange. [1][5]

The reason this matters is that HTTP isn't an implementation detail. It's the shape of the contract. Once I could see the request/response pair as a single, self-contained conversation, most of "API design" stopped being a pile of rules and became consequences of that shape — verbs mean what they do because the protocol defines them, status codes work because the response always carries one, headers exist because both sides need a place for metadata that isn't the body.

The request/response conversation

Every interaction is one request and one response [1]. A request has four parts:

POST /orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJ...

{"product": "book", "qty": 2}
  • A request line — method (POST), path (/orders), protocol version.
  • Headers — key/value metadata: what host, what content type, who's calling.
  • A blank line.
  • An optional body — the payload, here JSON.

The response mirrors it almost exactly, except the first line carries a status code instead of a method [1][4]:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /orders/417

{"id": 417, "status": "pending"}

Two details from this shape are load-bearing for everything else:

  • Stateless. Each request stands alone; the server keeps no memory between them [1]. Anything that needs to persist (login state, carts) has to be sent again every time — which is exactly why cookies, tokens, and sessions exist as workarounds bolted on top.
  • Typed verbs. The method isn't decorative — it declares intent. GET reads, POST creates, PUT replaces, PATCH partially updates, DELETE removes [2]. The server is free to ignore that intent, but clients and intermediaries (caches, proxies) rely on it.

Methods, and what they promise

The roadmap groups methods together because they're the verb in every operation [2]. The part I had to memorize cleanly is the two properties each verb carries, because they decide whether a request is safe to retry:

  • Safe — calling it doesn't change server state (read-only). GET, HEAD, OPTIONS.
  • Idempotent — calling it N times has the same effect as calling it once. GET, PUT, DELETE (and HEAD, OPTIONS). POST is not idempotent — submitting the same order twice creates two orders.
safety vs. idempotency SAFE UNSAFE IDEM- POTENT NOT IDEM. GET PUT DELETE POST

The practical consequence: a flaky network can safely auto-retry GET, PUT, and DELETE — same result. It must not blindly retry POST, because the second attempt might be a duplicate. That single distinction is why payment endpoints demand an idempotency key: it manually gives a non-idempotent verb the idempotency the protocol can't.

Headers: the metadata channel

The body is the payload; headers are everything else both sides need to say about it [3]. They're how the parties negotiate content, carry credentials, hint at caching, and pass tracing IDs. The ones I see in nearly every API:

  • Content-Type — what the body is (application/json). Without it, the server guesses.
  • Accept — what representation the client wants back. This is the lever for content negotiation: the same resource can return JSON, XML, or HTML depending on what the client asks for [3].
  • Authorization — who the caller is (Bearer <token>, Basic <base64>).
  • Cache-Control — how caches may treat the response.

The mental shift: headers aren't a dumping ground. They're a typed channel, and the protocol defines their meaning. Putting auth in a header instead of the body isn't style — it's because intermediaries (proxies, gateways) know to look there.

Status codes: the response always carries one

Every response begins with a 3-digit status, and the first digit is the only part I actually needed to internalize [4]:

  • 2xx — success (200 OK, 201 Created, 204 No Content).
  • 3xx — redirection (301, 304 Not Modified for caches).
  • 4xx — the client messed up (400 Bad Request, 401 Unauthorized, 404 Not Found, 429 Too Many Requests).
  • 5xx — the server messed up (500, 502, 503).

The line that matters for debugging: 4xx means the server understood the request and is refusing because of something the caller did or sent; 5xx means the server failed to handle it. A 404 isn't "the service is down" — it's "you asked for something that doesn't exist, fix your request." That single habit — classify by first digit before reaching for a fix — collapsed a lot of confused incident channels.

Versions: the substrate keeps moving

HTTP isn't frozen. HTTP/1.1 was the workhorse for two decades; HTTP/2 introduced multiplexing (many requests over one connection); HTTP/3 moved the transport onto QUIC for better performance on lossy networks [1][6]. For API design the practical impact is small — the request/response conversation looks the same to the application regardless of version — but two consequences are worth knowing. HTTP/2's multiplexing is what makes one-connection-per-client realistic, which matters for gRPC and for chatty APIs. And HTTP/3's connection migration helps mobile clients switching between cell and Wi-Fi. The protocol evolves underneath; the contract shape stays.

How I use this

Two habits fall straight out of the conversation model. When debugging a failing call, I read it as a single exchange: I check the request line and headers first (did I even call the right thing with the right content type?), then the status code's first digit (is this my fault or the server's?), and only then the body. When designing an endpoint, I fill in the four slots deliberately — which verb (so retries and caches behave), which headers (so auth and content type are explicit), which status (so the client can branch correctly), which body shape. The protocol gives me the skeleton; the discipline is just not leaving any of those slots to chance.

References

[1] Cloudflare, "What is HTTP?," 2024. [Online]. Available: https://www.cloudflare.com/en-gb/learning/ddos/glossary/hypertext-transfer-protocol-http/

[2] Mozilla, "HTTP request methods," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods

[3] Mozilla, "HTTP headers," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers

[4] Mozilla, "HTTP response status codes," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

[5] Mozilla, "An overview of HTTP," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview

[6] Smashing Magazine, "HTTP/3 From A To Z: Core Concepts," 2021. [Online]. Available: https://www.smashingmagazine.com/2021/08/http3-core-concepts-part1/

Knowledge check · Question 1 of 5

Which HTTP method is NOT idempotent?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!