Methods and Status Codes: Semantics, Not Decoration
An API can be designed like this:
POST /api
{"action":"getUser","id":42}
Technically it works.
But HTTP then stops knowing that the operation is a read.
Methods are not just names of endpoint actions. They carry semantics.
Safe
GET, HEAD and OPTIONS are safe methods.
Simplified:
client does not request a state-changing action
That lets browsers and intermediaries perform certain optimizations, prefetching or caching without assuming a mutation.
Which is why:
GET /delete-user?id=42
is semantically a bad idea even when the backend accepts it.
Idempotent
An operation is idempotent if performing the same request several times has the same intended effect as performing it once.
For example:
DELETE /users/42
The first request removes the user.
The second may return a different status, but the intended final state is still:
user 42 does not exist
PUT, DELETE and the safe methods have defined idempotent semantics.
POST does not.
Why should the frontend care?
The network can fail at an ambiguous moment:
client -> POST /payments
server -> payment created
network breaks before response
client <- ???
The frontend does not know:
request failed before execution?
or
response was lost after execution?
An automatic retry may perform the operation a second time.
So a retry policy should not rest solely on:
catch {
retry();
}
It has to take the semantics of the operation into account.
For non-idempotent operations applications often introduce idempotency keys of their own.
The status code is part of the contract too
This is not enough:
2xx = good
4xx = frontend fault
5xx = backend fault
Examples:
201 Created
-> a resource was created
202 Accepted
-> the request was accepted, work may continue
204 No Content
-> success with no body
304 Not Modified
-> use the existing representation from cache
401 Unauthorized
-> no valid authentication
403 Forbidden
-> the request was understood but is not allowed
409 Conflict
-> the request conflicts with the current state
For the UI, the difference between 202 and 201 can change the whole flow.
A 202 often leads to:
submit
|
v
operation accepted
|
v
poll / SSE / realtime update
|
v
operation completed
response.ok is deliberately simple
Fetch gives us:
response.ok
which is true for 200-299.
That is convenient, but it does not replace domain semantics.
if (!response.ok) {
// we still have to know what 409, 422, 429 mean
}
HTTP gives a shared language.
The application still has to give it product meaning.
The next element of that language is headers: the metadata that tells the browser and the backend how to interpret the request and the response.