Methods i status codes: semantyka, nie dekoracja
Można zaprojektować API tak:
POST /api
{"action":"getUser","id":42}
Technicznie działa.
Ale HTTP przestaje wtedy wiedzieć, że operacja jest odczytem.
Methods nie są tylko nazwami endpointowych akcji. Niosą semantykę.
Safe
GET, HEAD i OPTIONS należą do metod safe.
W uproszczeniu:
client does not request a state-changing action
To pozwala browserom i pośrednikom wykonywać pewne optymalizacje, prefetching czy caching bez zakładania mutacji.
Dlatego:
GET /delete-user?id=42
jest semantycznie złym pomysłem nawet wtedy, gdy backend go akceptuje.
Idempotent
Operacja jest idempotentna, jeśli wielokrotne wykonanie tego samego requestu ma ten sam zamierzony efekt co jedno wykonanie.
Przykład:
DELETE /users/42
Pierwszy request usuwa użytkownika.
Drugi może zwrócić inny status, ale zamierzony końcowy stan nadal jest:
user 42 does not exist
PUT, DELETE oraz safe methods mają zdefiniowaną semantykę idempotentną.
POST już nie.
Dlaczego frontend powinien o tym wiedzieć?
Sieć może zawieść w niejednoznacznym momencie:
client -> POST /payments
server -> payment created
network breaks before response
client <- ???
Frontend nie wie:
request failed before execution?
or
response was lost after execution?
Automatyczny retry może wykonać operację drugi raz.
Dlatego retry policy nie powinna opierać się wyłącznie na:
catch {
retry();
}
Musi uwzględniać semantykę operacji.
Dla nieidempotentnych operacji aplikacje często wprowadzają własne idempotency keys.
Status code też jest częścią kontraktu
Nie wystarczy:
2xx = good
4xx = frontend fault
5xx = backend fault
Przykłady:
201 Created
-> utworzono zasób
202 Accepted
-> request przyjęty, praca może trwać dalej
204 No Content
-> sukces bez body
304 Not Modified
-> użyj istniejącej reprezentacji z cache
401 Unauthorized
-> brak poprawnego uwierzytelnienia
403 Forbidden
-> żądanie rozpoznane, ale niedozwolone
409 Conflict
-> request koliduje z aktualnym stanem
Dla UI różnica między 202 i 201 może całkowicie zmienić flow.
202 często prowadzi do:
submit
|
v
operation accepted
|
v
poll / SSE / realtime update
|
v
operation completed
response.ok jest celowo proste
Fetch daje:
response.ok
czyli true dla 200-299.
To wygodne, ale nie zastępuje semantyki domeny.
if (!response.ok) {
// nadal musimy wiedzieć, co oznacza 409, 422, 429...
}
HTTP daje wspólny język.
Aplikacja nadal musi nadać mu znaczenie produktowe.
Kolejny element tego języka to headers: metadane, które mówią browserowi i backendowi, jak interpretować request oraz response.