Library
00/08 · ~38 min
GUIDEDECK · for APIs that behave the way callers expect

HTTP Request
Methods, semantics
& the contract of REST.

A 38-minute working session on the verbs of the web — GET, POST, PUT, PATCH, DELETE — the safety and idempotency rules behind them, the status codes that matter, how to model a resource so clients, caches and proxies all agree on what your endpoint does, and the tools and API styles you'll reach for in practice.

~38 MINBACKEND / FULL-STACKPROTOCOL-AGNOSTIC
SCROLL
01 · Why HTTP semantics matter 4 min

The method is a promise
about what the request does.

HTTP isn't just a pipe for moving bytes — it's a shared contract. Between your code and the server sits a crowd of helpers: the browser, a CDN (a network of edge servers that keep copies close to users), proxies and load balancers (relays that forward and spread out traffic), and your own retry logic. Each of them reads the method and decides, on its own, what it may do: store the answer, fetch it ahead of time (a prefetch), or send it again after a timeout. Break the promise the method makes and that whole crowd quietly works against you.

HTTPHyperText Transfer Protocol — is a request/response protocol where every request states a method (the verb), a target (the URL), some headers (metadata), and an optional body. The method declares intent; everything downstream optimizes around that declared intent.

Anatomy of an exchange

  • Request line — the method + path + version (GET /users/42 HTTP/1.1).
  • Headers — key/value metadata: who's asking, what format they accept, auth, caching hints.
  • Body — the payload (only some methods carry one).
  • The response mirrors it: a status line, headers, and a body.
REQUEST POST /orders HTTP/1.1 Host: api.shop.com Content-Type: app/json Authorization: Bearer … — HEADERS — { "item": "A12", "qty": 2 } — BODY — RESPONSE HTTP/1.1 201 Created Location: /orders/8801 Content-Type: app/json — HEADERS — { "id": 8801, "status": "open" } — BODY —

Method + path + headers + body in; status + headers + body back. The verb at the top governs everything.

Why the verb is load-bearing

Verb ignored — "everything is a GET"
// a link a crawler or prefetch can follow… GET /deleteUser?id=42 GET /transferMoney?to=99&amt=500 // GET is "safe" — so a browser prefetch, // a proxy, or a bot can fire this for you. // You just deleted a user by accident.
Verb honored — intent matches method
// the method states the effect; nothing // safe-by-default can trigger it. DELETE /users/42 POST /transfers // body: { to, amt } // caches won't store it, prefetch won't // fire it, and it reads correctly in a log.

Rule zero: the method must tell the truth about the effect. Everything in this talk follows from that one promise.

02 · The methods, one by one 7 min

Five verbs do the work;
two more answer about it.

GET, POST, PUT, PATCH and DELETE cover almost everything you'll build; HEAD and OPTIONS exist for metadata and negotiation. Click through each — note what it does to state and whether it carries a body.

A method (or verb) names the action to perform on the resource at the URL. The same path /users/42 means "read", "replace" or "remove" depending entirely on whether the verb is GET, PUT or DELETE. The noun is the URL; the verb is the method.

GET — read a resource, change nothing

GET /users/42 GET /users?role=admin&page=2 // no request body. Inputs go in the URL / query. // safe + idempotent + cacheable. // → 200 OK with the resource, or 404 if absent.
Use when
Fetching data — a record, a collection, a filtered list.
Never
Use GET to mutate. It's cached and retried freely; side effects will surprise you.

POST — create, or "process this"

POST /orders Content-Type: application/json { "item": "A12", "qty": 2 } // server assigns the id, returns it: // → 201 Created · Location: /orders/8801 // NOT idempotent: twice = two orders.
Use when
Creating a child in a collection, or any non-idempotent action (charge, send, submit).
Signature
The collection (/orders) decides the new id, not the client.

PUT — replace the whole resource

PUT /users/42 { "name": "Ada", "email": "ada@x.io", "role": "admin" } // the FULL representation — omitted fields are wiped. // idempotent: same PUT twice = same end state. // → 200 OK (updated) or 201 (created at that id).
Use when
You hold the complete new state and the client owns the id.
Gotcha
Send a partial body and you blank the missing fields. That's what PATCH is for.

PATCH — apply a partial update

PATCH /users/42 { "role": "editor" } // only the named field changes; the rest is untouched. // → 200 OK with the updated resource. // idempotent IF it sets values (role = "editor"). // NOT idempotent for deltas (balance += 10).
Use when
Changing one or two fields without resending the entire object.
It's really
PUT's scalpel — same target, smaller blast radius.

DELETE — remove the resource

DELETE /users/42 // no body needed; the URL is the target. // idempotent: deleting twice ends the same way. // → 200/204 the first time… // → 404 the second is fine — state is identical.
Use when
Removing a single resource by its URL.
Idempotent?
Yes — the state after one or ten DELETEs is the same: gone.

HEAD & OPTIONS — ask about the resource

HEAD /report.pdf // like GET, headers only — no body // → 200 + Content-Length: 4.2MB (check before downloading) OPTIONS /orders // what can I do here? // → Allow: GET, POST, OPTIONS // also drives the browser's CORS preflight.
HEAD
Cheap existence / size / caching checks without transferring the body.
OPTIONS
Asks "what can I do here?" It also powers the browser's CORS preflight — a quick permission check the browser fires before a cross-site request, to confirm the server actually allows it.
03 · Safety & idempotency 6 min

Two properties decide whether
a retry is safe.

Networks drop responses all the time. Whether your client, a proxy, or a load balancer can safely repeat a request comes down to two words — and getting them wrong is how you end up charging a card twice.

Safe — the request doesn't change server state (a pure read). Idempotent — making the same request N times leaves the same end state as making it once. Every safe method is idempotent; the reverse isn't true (PUT and DELETE change state, yet repeat cleanly).

The mental-model table

GET · HEAD

Safe · Idempotent

Pure reads. Cache them, prefetch them, retry them — nothing downstream changes. Cacheable by default.

PUT · DELETE

Safe · Idempotent

They mutate, but the end state is stable: PUT this exact body twice → same record; DELETE twice → still gone. Retries are safe.

POST · PATCH*

Safe · Idempotent

POST twice makes two resources. A delta-PATCH (balance += 10) compounds. Repeat only with an idempotency guard. *set-PATCH is idempotent

client times out server request #1 response ✕ lost retry (same request) POST → 2 orders ✕ PUT → 1 record ✓

The response is lost, not the request. The retry is identical — so idempotency is the only thing standing between you and a duplicate.

Making POST safe to retry

When you genuinely need a non-idempotent create to survive retries, let the client send an idempotency key. The server records it and returns the original result on a repeat — no duplicate.

POST /charges Idempotency-Key: 5f2c-…-a91 // client-generated, unique { "amount": 500 } // server: seen this key? → replay the saved 201. // never? → process once, store result under the key.

Like  a coat-check ticket — hand over the same stub twice, you still get back the one coat.

04 · Status codes that matter 5 min

The response's first line
is its verdict.

A status code is a three-digit summary the whole stack understands. You don't need all 60-odd — you need to use the right class, and the dozen codes you'll actually return.

A status code classifies the outcome by its first digit: 2xx success, 3xx redirection, 4xx the caller got it wrong, 5xx the server failed. Pick the class correctly and clients, monitoring and retries all do the right thing automatically.
2
Success
it worked
3
Redirection
look elsewhere
4
Client error
your request is wrong
5
Server error
our fault, retry maybe
2
2xx — Success
The request was received, understood and accepted.
+
  • 200 OK — the default success; body carries the result (GET, PUT, PATCH).
  • 201 Created — a POST made a new resource; return its Location header.
  • 202 Accepted — queued for async work; not done yet, but taken.
  • 204 No Content — success with nothing to send back (a common DELETE response).
3
3xx — Redirection
The resource lives somewhere else, or hasn't changed.
+
  • 301 Moved Permanently — update your links; caches and SEO follow it forever.
  • 302 / 307 Found / Temporary — go here for now; 307 preserves the method.
  • 304 Not Modified — your cached copy is still fresh; no body sent. The backbone of conditional GETs (Part 6).
4
4xx — Client error
Don't retry unchanged — fix the request first.
+
  • 400 Bad Request — malformed syntax or invalid payload.
  • 401 Unauthorized — you're not authenticated (really "unauthenticated").
  • 403 Forbidden — authenticated, but not allowed.
  • 404 Not Found · 409 Conflict (version clash) · 422 Unprocessable (valid syntax, failed rules) · 429 Too Many Requests (rate-limited).
5
5xx — Server error
The request was fine; the server failed it. Safe to retry idempotent verbs.
+
  • 500 Internal Server Error — the catch-all bug; never leak a stack trace in the body.
  • 502 Bad Gateway · 503 Service Unavailable — an upstream is down or overloaded; pair 503 with Retry-After.
  • 504 Gateway Timeout — an upstream didn't answer in time.

The split that matters most for automation: 4xx = don't retry, fix the request; 5xx = the server failed, retrying an idempotent verb is fine.

05 · Modeling resources 5 min

URLs are nouns;
methods are the verbs.

REST's core trick: don't put the action in the URL. Name a resource, then let the HTTP method say what to do with it. One consistent shape replaces a sprawl of custom endpoints.

RESTRepresentational State Transfer — models your system as resources (things) identified by URLs. You move their state around by exchanging representations (usually JSON) using the standard methods. The verb is never in the path — the path only names the noun.
RPC-in-the-URL — a verb per action
POST /createUser POST /getUser?id=42 POST /updateUserEmail POST /deleteUserById GET /listUsersByRoleAdmin // every action is a new endpoint to learn, // document, secure and cache separately.
Resource + method — one shape, reused
POST /users // create GET /users/42 // read one GET /users?role=admin // filter the collection PATCH /users/42 // partial update DELETE /users/42 // remove // same nouns, predictable everywhere.
/users COLLECTION /users/42 ITEM /users/42/orders SUB-COLLECTION GET → list POST → add GET → read PUT → replace PATCH → tweak DELETE → remove

A collection holds items; items can hold sub-collections. The same six methods apply at every level.

URL conventions that age well

  • Plural nouns for collections — /users, /orders — not /getUser.
  • Hierarchy for ownership/users/42/orders reads as "user 42's orders".
  • Query string for filtering, sorting, paging?role=admin&sort=name&page=2 — not new paths.
  • Lowercase, hyphenated, no trailing verbs/purchase-orders, never /PurchaseOrders/create.

Like  a filing cabinet: drawers and folders are nouns; what you do (file, read, shred) is the action you bring.

06 · Headers, negotiation & caching 4 min

Headers are how the request
and response negotiate.

The body is the payload; the headers are the contract around it — what format, what auth, how long it stays fresh. Two patterns earn their keep daily: content negotiation and caching.

Content negotiation

The client asks; the server answers in kind

The client states what it accepts; the server declares what it sent. Same URL can serve JSON, CSV, or a different language depending on the ask.

// request — "I'd like JSON, in English" Accept: application/json Accept-Language: en // response — "here's what I'm giving you" Content-Type: application/json; charset=utf-8
Caching

Say how fresh, and how to revalidate

Cache-Control sets the freshness budget; an ETag is a fingerprint of the body. Together they let a client skip the download when nothing changed.

// response — cache 5 min, here's the fingerprint Cache-Control: max-age=300 ETag: "v23-9af1" // next request — "only send if it changed" If-None-Match: "v23-9af1" // → 304, no body
client server GET · If-None-Match: "v23-9af1" 304 Not Modified — no body ETag matches → reuse cache bandwidth saved, round-trip tiny

The client sends its ETag back; if it still matches, the server answers 304 with no body — a near-free response.

Why this is wired to the verb

  • Only safe methods (GET/HEAD) are cacheable by default — the spec lets a cache store them precisely because they don't mutate.
  • A successful PUT/PATCH/DELETE should invalidate the cached representation of that URL.
  • Conditional requests (If-Match) also prevent lost updates — reject a PUT whose ETag is stale with a 409 Conflict.

Semantics unlock the network. Honest verbs let caches, CDNs and conditional requests do real work for free — break them and you opt out of all of it.

07 · Tooling & API styles 4 min

The methods are the theory;
these are the tools you'll touch.

Everything so far is protocol. Day to day you'll poke an API with a client, describe it with a spec, and pick an overall style. Here are the leading options, with one honest upside and one honest downside each — and a plain rule for choosing.

An APIApplication Programming Interface — is just the set of endpoints one program offers another to talk to it. A client is the tool you use to send those requests by hand; a spec is a written description of the API; and a style is the overall shape you build it in. Three decisions, covered below.

API clients — how you send a request by hand

curl

The command-line workhorse

A tiny terminal program, already on almost every machine, that speaks raw HTTP. You type the method, URL, headers and body yourself.

  • Pro — scriptable and zero-setup; perfect for automation, servers and pasting an exact repro into a bug report.
  • Con — no interface; hand-building headers and large JSON bodies gets fiddly fast.
Postman

The full graphical workbench

The most popular point-and-click API client. You build, save and share requests, group them into collections, and add tests and mock servers.

  • Pro — rich UI, shared collections, environments and a huge ecosystem for whole teams.
  • Con — heavyweight and account-centric; the sign-in and cloud sync feel like overkill for a quick poke.
Insomnia

The lighter, local-first client

An open-source graphical client, leaner than Postman, with first-class support for both REST and GraphQL and easy OpenAPI import.

  • Pro — clean, fast and works fully offline; great GraphQL experience.
  • Con — smaller ecosystem and fewer team / collaboration features than Postman.

How to choose: reach for curl to automate and debug, Postman when a team needs to share a big documented API, and Insomnia when you want a fast local client or live in GraphQL.

OpenAPI (its older name is Swagger) is the de-facto standard for API specs: one machine-readable file (YAML or JSON) that lists every path, method, input and response your API has. Write it once and tooling generates interactive docs, ready-made client libraries, mock servers and request validators from it — so the description and the real API can't drift apart. Pro: one source of truth, lots of free tooling. Con: a large spec needs real discipline to keep accurate.

API styles — the overall shape

REST

Resources at URLs

Nouns in the path, HTTP methods as the verbs — exactly what this whole talk has been about. The default for most web APIs.

  • Pro — simple, universal and cache-friendly; readable in a browser and every tool.
  • Con — fixed responses mean you often get too much or too little; busy screens need several round trips.
GraphQL

One endpoint, you ask for fields

A single URL where the client sends a query naming exactly the fields it wants, and gets back just those — no more, no less.

  • Pro — no over-fetching; one request can gather data that REST would need several calls for.
  • Con — caching and rate-limiting get harder, and a careless query can be expensive to run.
gRPC

Typed calls between services

A binary, contract-first style (over HTTP/2) where you call typed service methods — UserService.Get() — almost like calling a local function.

  • Pro — very fast and compact, strongly typed, with streaming built in.
  • Con — not human-readable, awkward straight from a browser, and a steeper setup.

How to choose: REST for public, resource-shaped APIs; GraphQL when clients need flexible, deeply nested data; gRPC for fast internal service-to-service traffic.

client REST · 3 round trips GET /users/42 GET /users/42/orders GET /products GraphQL · 1 query POST /graphql { user, orders, products } gRPC · 1 typed call UserService.Get() binary · HTTP/2

Same data, three shapes: REST makes several calls, GraphQL asks for it all in one query, gRPC makes a single typed call.

Reading the picture

  • REST spreads the data across several URLs — easy to cache, but more trips for one screen.
  • GraphQL folds it into one request you describe yourself — fewer trips, but you own the cost of the query.
  • gRPC replaces URLs with typed method calls — fastest, but meant for service-to-service, not a browser.

Like  ordering lunch: REST is three separate counters, GraphQL is one custom order, gRPC is a standing arrangement with the kitchen.

08 · Designing a real endpoint 3 min

Bad → good:
one endpoint, done right.

Everything so far lands in a single design choice. Take "cancel an order" and watch the rules apply at once.

Ignores the contract
GET /orders/cancel?id=88 // ✕ GET that mutates — caches & prefetch can fire it // ✕ verb in the URL, id in the query // ✕ returns 200 with { "error": "not allowed" } // ✕ retry on timeout → cancels again, maybe refunds twice
Honors the contract
POST /orders/88/cancellation Idempotency-Key: 7c1e-…-04 // ✓ a cancellation is a resource you create // ✓ → 201 Created, or 409 if already shipped // ✓ wrong owner → 403; missing order → 404 // ✓ idempotency-key makes the retry a no-op
1The verb tells the truth. GET reads, the others mutate — never hide an effect behind a safe method.
2Know safe vs idempotent. It decides what your retries, caches and proxies are allowed to do.
3Pick the right status class. 2xx works, 4xx is the caller, 5xx is you — automation depends on it.
4Model nouns, not actions. Resources in the URL, behavior in the method, filters in the query string.
5Let the network help. Honest semantics + ETags + idempotency keys give you caching and safe retries for free.

Keep going

  • MDN HTTP reference — methods, status codes, headers, authoritative and free.
  • RFC 9110 — HTTP Semantics — the spec itself, surprisingly readable.
  • httpwg.org & http.cat — the working group, and status codes you'll actually remember.

One sentence to remember

"The URL is the noun, the method is the verb — make them both tell the truth."

— the whole talk, in one line

Knowledge check

Did it stick?

Five quick questions on HTTP methods, safety & idempotency, status codes and REST — instant feedback, no sign-in.

Rate this deck
be the first

Navigate with ← → or scroll · back to library