Konrad Kowalski (rootsher)Principal Platform & Reliability Architect011000100110110101011101011101101000011101011101

Methods i status codes: semantyka, nie dekoracja

data
kategoria
Backend
także w
Frontend
czytanie
1 min / 219 słów

Można zaprojektować API tak:

http
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:

text
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:

http
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:

http
DELETE /users/42

Pierwszy request usuwa użytkownika.

Drugi może zwrócić inny status, ale zamierzony końcowy stan nadal jest:

text
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:

text
client -> POST /payments
server -> payment created
network breaks before response
client <- ???

Frontend nie wie:

text
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:

js
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:

text
2xx = good
4xx = frontend fault
5xx = backend fault

Przykłady:

text
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:

text
submit
  |
  v
operation accepted
  |
  v
poll / SSE / realtime update
  |
  v
operation completed

response.ok jest celowo proste

Fetch daje:

js
response.ok

czyli true dla 200-299.

To wygodne, ale nie zastępuje semantyki domeny.

js
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.