Przejdź do treści

REST API — zasady projektowania i dobre praktyki

REST API zaprojektowane naprędce wróci do Ciebie z długiem. Konwencje, wersjonowanie i obsługa błędów — zasady, których tutoriale zwykle pomijają.

Maciej Sala

Founder StriveLab

11 min czytaniaOpublikowano 5 grudnia 2025 (Aktualizacja 27 maja 2026)

Ale REST to nie tylko "używaj HTTP". To zbiór konwencji i zasad, które sprawiają, że API jest intuicyjne, przewidywalne i łatwe w użyciu.

W tym artykule poznasz zasady projektowania — zarówno jako konsument (frontend), jak i twórca (backend). Szerszy kontekst aplikacyjny znajdziesz w przewodniku Backend dla frontendowca: serwer, bazy danych i API.

Zasady REST API, które stabilizują projekt

REST opisuje sześć właściwości, które razem tworzą architekturę zwaną Representational State Transfer. Warto je znać nie po to, żeby cytatować ze specyfikacji, ale żeby rozumieć, dlaczego pewne decyzje projektowe są lepsze od innych.

Client-server w REST API

Klient (frontend) i serwer (backend) są od siebie oddzielone i komunikują się przez HTTP. Ta separacja oznacza, że frontend może ewoluować niezależnie od backendu — wystarczy zachować kontrakt API. Możesz na przykład przepisać całe na inny framework bez dotykania backendu, albo wymienić silnik bazy danych bez wpływu na klientów.

Stateless API: brak sesji po stronie serwera

Każdy request powinien zawierać wszystko, czego serwer potrzebuje do jego obsługi — bez polegania na stanie sesji po stronie serwera. W praktyce wiele API łączy to z tokenami w nagłówkach Authorization, co pozwala na stateless uwierzytelnianie. Jeśli API korzysta z sesji serwerowych lub cookies, jest to technicznie odejście od czystego REST, ale w realu to częsty kompromis — ważniejsze jest, żeby kontrakt był spójny i dobrze udokumentowany.

Cacheable: cache HTTP w REST API

Odpowiedzi powinny jawnie informować klientów, czy można je cachować i jak długo. Nagłówki Cache-Control i ETag to podstawa — dzięki nim przeglądarka, lub reverse proxy mogą serwować odpowiedzi bez angażowania backendu. Dla API publicznych to realny wpływ na koszty infrastruktury i czas odpowiedzi.

Uniform Interface: spójny kontrakt REST API

Jednolity interfejs to serce REST — zasoby identyfikowane przez URL, operacje przez metody HTTP, standaryzowane kody statusu. Dzięki tej jednolitości deweloper integrujący nowe API może w dużej mierze przewidzieć jego zachowanie bez czytania dokumentacji od deski do deski.

Layered System: warstwy w architekturze API

Klient nie powinien zakładać, że komunikuje się bezpośrednio z serwerem docelowym. Pomiędzy nimi mogą stać load balancery, cache'e, API gateway czy reverse proxy — i to jest poprawna, wręcz pożądana architektura. Dobry design REST uwzględnia, że warstwa pośrednia może np. cachować GET-y albo rozdzielać ruch.

Code on Demand jako opcjonalna zasada REST

Serwer może opcjonalnie przekazywać klientowi wykonywalny kod (np. JavaScript). W praktyce ta zasada jest rzadko stosowana i często pomijana w opisach REST.

Struktura URL w REST API

Zasoby w URL, czyli resources w REST API

Pierwsza zasada, której złamanie natychmiast widać w code review: URL powinien nazywać zasób, a nie akcję. Metoda HTTP jest od opisywania co robisz — URL jest od opisywania z czym.

Code
✅ Dobrze (rzeczowniki):
GET /users
GET /users/123
GET /users/123/posts

❌ Źle (czasowniki):
GET /getUsers
GET /getUserById/123
POST /createUser

Wzorzec z czasownikami pochodzi z epoki SOAP i RPC — w REST nie ma dla niego miejsca. Jeśli masz ochotę napisać /deleteUser/123, to sygnał, że powinieneś napisać DELETE /users/123.

Kolekcje i pojedyncze zasoby w endpointach

Ten podział jest fundamentem REST: kolekcja zasobów ma inny URL niż pojedynczy zasób, a operacje na obu wyglądają inaczej.

Code
/users          → kolekcja wszystkich użytkowników
/users/123      → pojedynczy użytkownik o ID 123
/users/123/posts → posty użytkownika 123

POST /users tworzy nowy zasób w kolekcji. DELETE /users/123 usuwa konkretny zasób z kolekcji. Frontend integrujący takie API może wywnioskować semantykę bez czytania dokumentacji.

Zagnieżdżone zasoby i relacje w REST API

Zagnieżdżenie w URL wyraża relację między zasobami — komentarze należą do posta, posty należą do użytkownika:

Code
GET /users/123/posts         → posty użytkownika
GET /users/123/posts/456     → konkretny post użytkownika
GET /posts/456/comments      → komentarze do posta

Zasada praktyczna: zatrzymaj się na maksymalnie 2-3 poziomach. Głębsze drzewo (/users/123/posts/456/comments/789/replies) jest trudne do czytania i sugeruje, że relacja powinna być modelowana inaczej — np. przez filtrowanie: GET /replies?commentId=789.

Konwencje nazewnictwa endpointów REST

Konsekwencja w nazewnictwie jest ważniejsza niż wybór konkretnej konwencji — ale warto wybrać tę, którą web de facto stosuje:

Code
✅ Dobrze:
/users              (liczba mnoga)
/blog-posts         (kebab-case)
/user-profiles

❌ Źle:
/user               (liczba pojedyncza)
/blogPosts          (camelCase)
/user_profiles      (snake_case w URL)

Liczba mnoga dla kolekcji odzwierciedla fakt, że zasób to zbiór rekordów, nie jeden rekord. Kebab-case jest standardem webowym — camelCase jest dla właściwości JSON, nie segmentów URL.

Metody HTTP i CRUD w REST API

Metoda HTTP wyraża intencję operacji — to ta część requestu, która odpowiada na pytanie "co chcesz zrobić z tym zasobem?". Mapowanie na jest intuicyjne, ale ma kilka niuansów, które warto znać zanim zaczniesz projektować API.

MetodaCRUDOpisIdempotentna?Przykład
GETReadPobierz zasóbTakGET /users/123
POSTCreateUtwórz zasóbNiePOST /users
PUTUpdateZastąp zasóbTakPUT /users/123
PATCHUpdateCzęściowa aktualizacjaZależyPATCH /users/123
DELETEDeleteUsuń zasóbTakDELETE /users/123

Idempotencja oznacza, że wielokrotne wywołanie tej samej operacji daje ten sam efekt. Jeśli klient wyśle DELETE /users/123 dwa razy — drugi raz powinien dostać 404, ale stan serwera jest ten sam. Przy POST każde wywołanie tworzy nowy zasób — retry bez zabezpieczenia to duplikat.

GET w REST API — pobieranie danych

Code
# Kolekcja
GET /users
Response: 200 OK
{
  "data": [
    { "id": 1, "name": "Jan" },
    { "id": 2, "name": "Anna" }
  ]
}
 
# Pojedynczy zasób
GET /users/1
Response: 200 OK
{ "id": 1, "name": "Jan", "email": "jan@example.com" }
 
# Nieistniejący zasób
GET /users/999
Response: 404 Not Found

POST w REST API — tworzenie zasobu

Code
POST /users
Content-Type: application/json
 
{ "name": "Piotr", "email": "piotr@example.com" }
 
Response: 201 Created
Location: /users/3
{ "id": 3, "name": "Piotr", "email": "piotr@example.com" }

PUT w REST API — pełna aktualizacja zasobu

Code
PUT /users/3
Content-Type: application/json
 
{ "name": "Piotr Nowak", "email": "piotr.nowak@example.com" }
 
Response: 200 OK
{ "id": 3, "name": "Piotr Nowak", "email": "piotr.nowak@example.com" }

PUT zazwyczaj oznacza pełną reprezentację zasobu — jeśli nie przekażesz jakiegoś pola, serwer powinien je wyzerować lub usunąć. To jest często źródło bugów, gdy frontend wysyła tylko zmodyfikowane pola, a serwer traktuje brakujące jako "usuń". Dlatego PUT warto stosować tylko wtedy, gdy faktycznie chcesz zastąpić cały zasób.

PATCH w REST API — częściowa aktualizacja zasobu

Code
PATCH /users/3
Content-Type: application/json
 
{ "email": "nowy.email@example.com" }
 
Response: 200 OK
{ "id": 3, "name": "Piotr Nowak", "email": "nowy.email@example.com" }

PATCH aktualizuje tylko przekazane pola. To najwygodniejsza metoda dla typowych operacji — zmiana emaila, statusu, jednej właściwości — bez ryzyka przypadkowego wyzerowania reszty obiektu. Warto jednak pamiętać, że semantyka PATCH nie jest tak rygorystycznie ustandaryzowana jak PUT — różne API implementują ją różnie, dlatego dokładne zachowanie powinno być opisane w dokumentacji.

DELETE w REST API — usuwanie zasobu

Code
DELETE /users/3
 
Response: 204 No Content

Kody statusu HTTP w REST API

Kody statusu to pierwsza linia komunikacji między serwerem a klientem — zanim frontend przeczyta body odpowiedzi, sprawdza kod. Właściwe kody pozwalają frontendowi podjąć decyzję (pokaż błąd, przekieruj na login, odśwież token) bez parsowania treści. Błędne kody — np. 200 z błędem w body — łamią tę konwencję i utrudniają cachowanie, obsługę błędów i debugowanie.

Statusy 2xx — poprawna odpowiedź API

KodZnaczenieKiedy
200OKGET, PUT, PATCH sukces
201CreatedPOST sukces (nowy zasób)
204No ContentDELETE sukces, brak body

201 Created powinien pojawić się przy każdym POST, który tworzy zasób. Warto przy nim zwracać nagłówek Location z URL nowego zasobu — klient wie wtedy, gdzie sięgnąć po szczegóły bez dodatkowego GET-a. 204 No Content jest właściwą odpowiedzią na DELETE — operacja zakończyła się sukcesem, ale nie ma nic do zwrócenia.

Statusy 4xx — błędy klienta w API

KodZnaczenieKiedy
400Bad RequestNieprawidłowe dane
401UnauthorizedBrak/nieprawidłowy token
403ForbiddenBrak uprawnień
404Not FoundZasób nie istnieje
409ConflictKonflikt (duplikat)
422Unprocessable EntityBłąd walidacji
429Too Many RequestsRate limit

Dwa kody, które regularnie powodują zamieszanie:

401 vs 403 — 401 znaczy "nie wiem kim jesteś, zaloguj się", 403 znaczy "wiem kim jesteś, ale nie wolno Ci". Różnica jest fundamentalna dla frontend handlera: przy 401 powinno nastąpić przekierowanie na login lub odświeżenie tokenu, przy 403 wystarczy pokazać komunikat o braku uprawnień.

400 vs 422 — 400 to ogólny błąd formatu requestu (niepoprawny JSON, brakujące wymagane nagłówki), 422 to poprawny syntaktycznie request, który nie przeszedł walidacji biznesowej (np. email jest stringiem, ale nie ma znaku @). W praktyce wiele API używa tylko 400 dla obu przypadków — ważniejsze niż wybór kodu jest spójny, szczegółowy format błędu w body.

409 Conflict jest właściwy przy próbie stworzenia duplikatu (użytkownik z tym samym emailem już istnieje) lub przy konflikcie optymistycznego lockingu.

Statusy 5xx — błędy serwera w API

KodZnaczenieKiedy
500Internal Server ErrorNieoczekiwany błąd
502Bad GatewayProblem z upstream
503Service UnavailableSerwer przeciążony

Błędy 5xx zawsze należą do serwera — klient nie może z nimi nic zrobić poza poczekaniem i ponowieniem. 503 Service Unavailable powinien być zwracany z nagłówkiem Retry-After, żeby klient wiedział, kiedy próbować ponownie. Nigdy nie zwracaj 5xx dla błędów walidacji czy braku uprawnień — to 4xx, i to ważne rozróżnienie przy monitoringu i alertach.

Format odpowiedzi JSON w REST API

Dobry format odpowiedzi to taki, którego klient nie musi zgadywać. Jeśli sukces wygląda inaczej przy GET a inaczej przy POST, a błąd ma różną strukturę w zależności od endpointa — integracja staje się obroną przed własnym API.

Format sukcesu w odpowiedzi API

Code
{
  "id": 123,
  "name": "Jan Kowalski",
  "email": "jan@example.com",
  "createdAt": "2025-01-15T10:30:00Z"
}

Daty powinny być zawsze w formacie ISO 8601 (2025-01-15T10:30:00Z) — łatwe do parsowania w każdym języku, jednoznaczne co do strefy czasowej.

Format błędu w REST API zgodny z RFC 9457

Standard RFC 9457 (Problem Details for HTTP APIs) definiuje jednolity format dla błędów API. Zamiast wymyślać własną strukturę, warto trzymać się tego standardu:

Code
{
  "type": "https://example.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "One or more fields failed validation.",
  "instance": "/api/v1/users",
  "errors": [
    { "field": "email", "message": "Invalid email format" },
    { "field": "name", "message": "Name is required" }
  ]
}

Pola type (URI identyfikujące klasę błędu), title (czytelna dla człowieka nazwa klasy), status (kod HTTP) i detail (szczegółowy opis) tworzą maszynoczytelną i jednocześnie czytelną dla dewelopera strukturę. instance wskazuje konkretne wystąpienie błędu, co ułatwia debugowanie i korelację z logami.

Nawet jeśli nie chcesz pełnego RFC 9457, trzymaj się jednej zasady: spójny format błędów przez całe API. Mieszanie { "error": "..." }, { "message": "..." } i { "errors": [...] } w różnych endpointach to klasyczny dług techniczny.

Kolekcja z metadanymi i paginacją

Code
{
  "data": [
    { "id": 1, "name": "Jan" },
    { "id": 2, "name": "Anna" }
  ],
  "meta": {
    "total": 100,
    "page": 1,
    "perPage": 10,
    "totalPages": 10
  }
}

Wrapper data + meta jest przydatny, bo pozwala rozszerzyć odpowiedź o metadane bez breaking change — wystarczy dodać nowe pole do meta.

Filtrowanie, sortowanie i paginacja w REST API

Filtrowanie przez query parameters

Code
GET /users?status=active
GET /users?role=admin&status=active
GET /products?category=electronics&minPrice=100&maxPrice=500
GET /posts?author=123&createdAfter=2025-01-01

Sortowanie wyników w REST API

Code
GET /users?sort=name           # rosnąco
GET /users?sort=-createdAt     # malejąco (minus)
GET /users?sort=role,-name     # wielokrotne

Alternatywna konwencja:

Code
GET /users?sortBy=name&order=asc

Paginacja offset i cursor w REST API

Offset-based:

Code
GET /users?page=2&perPage=10
GET /users?offset=10&limit=10

Cursor-based (lepsze dla dużych zbiorów):

Code
GET /users?cursor=abc123&limit=10

Offset pagination jest prostsza do implementacji i wygodna przy panelach admina, gdzie użytkownik chce przejść na konkretną stronę wyników. Ma jednak wadę przy dużych, zmieniających się zbiorach: jeśli ktoś doda rekord między requestami, page 2 może zawierać duplikaty z page 1 albo pominąć rekord.

Cursor pagination rozwiązuje ten problem — każda strona zaczyna od konkretnego punktu w zbiorze (zwykle ID lub timestamp ostatniego rekordu), niezależnie od tego, co się zdarzyło w zbiorze między requestami. To właściwy wybór dla nieskończonego przewijania, real-time feedów i API z wysoką częstotliwością zmian danych.

Odpowiedź z paginacją:

Code
{
  "data": [...],
  "meta": {
    "total": 100,
    "page": 2,
    "perPage": 10,
    "totalPages": 10,
    "nextPage": "/users?page=3&perPage=10",
    "prevPage": "/users?page=1&perPage=10"
  }
}

Wybór pól, czyli sparse fieldsets

Code
GET /users?fields=id,name,email
GET /users/123?fields=name,avatar

To wygodne, ale warto whitelistować pola po stronie serwera, żeby klient nie mógł wyciągać wszystkiego bez kontroli.

Include i eager loading w odpowiedzi API

Code
GET /posts/123?include=author,comments

Tak samo jak przy fields, warto posiadać limit dopuszczalnych relacji, żeby nie zrobić przypadkiem własnego "mini-GraphQL bez guardrailów".

Code
{
  "id": 123,
  "title": "Post",
  "author": { "id": 1, "name": "Jan" },
  "comments": [...]
}

Wersjonowanie REST API

Wersjonowanie API w URL

Code
GET /api/v1/users
GET /api/v2/users

Wersjonowanie API w nagłówku HTTP

Code
GET /api/users
Accept: application/vnd.myapp.v2+json
# lub dedykowany nagłówek:
API-Version: 2

Wersjonowanie API w query parameter

Code
GET /api/users?version=2

Rekomendacja: Wersjonowanie w URL jest najprostsze i najbardziej widoczne, ale nie każde API musi zaczynać od /v1, dlatego czasami wystarczy po prostu dobra kompatybilność wstecz i przemyślane polityki zmian.

Autoryzacja w REST API

Bearer Token i JWT w REST API

Code
GET /api/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

API Key w REST API

Code
GET /api/users
X-API-Key: your-api-key

Odpowiedzi API przy autoryzacji

Code
# Brak tokenu
401 Unauthorized
{ "error": "Authentication required" }
 
# Token wygasł
401 Unauthorized
{ "error": "Token expired" }
 
# Brak uprawnień
403 Forbidden
{ "error": "Insufficient permissions" }

Kiedy wersjonować REST API i jak robić to bez chaosu

Wersjonowanie API to jeden z tych tematów, które boli dopiero wtedy, gdy jest za późno. Jeśli masz publiczne API i wprowadzisz breaking change bez wersjonowania, psujesz wszystkich konsumentów naraz.

Breaking change to m.in.:

  • zmiana nazwy pola lub usunięcie pola z odpowiedzi
  • zmiana typu pola (string → number)
  • zmiana znaczenia kodu statusu dla danego endpointa
  • usunięcie endpointa

Non-breaking change to m.in.:

  • dodanie nowego opcjonalnego pola w odpowiedzi
  • dodanie nowego endpointa
  • dodanie opcjonalnego query parametru

Wersjonowanie w URL (/api/v1/users) jest najpopularniejsze i najprostsze — widać je w URL, łatwo testować, logować i routować na różne implementacje. Wadą jest to, że trzeba utrzymywać kilka wersji jednocześnie. Praktyczna zasada: zacznij od /v1 przy pierwszym publicznym release'ie, nawet jeśli nie masz jeszcze /v2 w planach. Dodanie wersjonowania do bezwersyjnego API jest samo w sobie breaking change.

Rate limiting w REST API

API powinno informować o limitach:

Code
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000

Po przekroczeniu:

Code
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
  "error": "Rate limit exceeded",
  "retryAfter": 60
}

Idempotency key dla wrażliwych POST-ów

Tworzenie zasobów po POST nie jest idempotentne, przy płatnościach, webhookach albo retriable requestach warto dodać klucz idempotencyjny:

Code
POST /payments
Idempotency-Key: 6d2f7b2f-8f16-4f1b-a5a8-123456789abc

Serwer powinien rozpoznać ponowienie tego samego żądania i nie wykonać operacji drugi raz.

HATEOAS w REST API jako opcja, nie obowiązek

Hypermedia As The Engine Of Application State, czyli linki do powiązanych zasobów:

Code
{
  "id": 123,
  "name": "Jan",
  "_links": {
    "self": { "href": "/users/123" },
    "posts": { "href": "/users/123/posts" },
    "followers": { "href": "/users/123/followers" }
  }
}

W praktyce jest to rzadko w pełni implementowane, ale warto chociaż poznać koncept.

Przykład REST API dla bloga

Endpointy API bloga

Code
# Posty
GET    /api/v1/posts              # lista postów
GET    /api/v1/posts/:id          # pojedynczy post
POST   /api/v1/posts              # utwórz post
PUT    /api/v1/posts/:id          # aktualizuj post
DELETE /api/v1/posts/:id          # usuń post

# Komentarze
GET    /api/v1/posts/:id/comments          # komentarze do posta
POST   /api/v1/posts/:id/comments          # dodaj komentarz
DELETE /api/v1/posts/:id/comments/:cid     # usuń komentarz

# Użytkownicy
GET    /api/v1/users/:id          # profil użytkownika
GET    /api/v1/users/:id/posts    # posty użytkownika

Przykładowe żądania do REST API

Code
# Lista postów z filtrowaniem i paginacją
GET /api/v1/posts?status=published&sort=-createdAt&page=1&perPage=10
 
# Utwórz post
POST /api/v1/posts
Authorization: Bearer <token>
Content-Type: application/json
 
{
  "title": "Mój nowy post",
  "content": "Treść posta...",
  "tags": ["javascript", "react"]
}
 
# Odpowiedź
201 Created
{
  "id": 456,
  "title": "Mój nowy post",
  "slug": "moj-nowy-post",
  "content": "Treść posta...",
  "author": {
    "id": 123,
    "name": "Jan"
  },
  "tags": ["javascript", "react"],
  "createdAt": "2025-01-15T10:30:00Z"
}
Połączenie perspektywy produktu, dewelopera i marketingu w jednym miejscu
Konsultacje

Chcesz zbudować własne API? Przejdź do Backend dla frontendowca: serwer, bazy danych i API.

Często zadawane pytania

Czy REST wymaga idealnego trzymania się teorii?

W praktyce większość API w webie to HTTP API inspirowane REST. Ważniejsza od puryzmu jest konsekwencja, przewidywalne zasoby, statusy HTTP i dobra dokumentacja.

Kiedy używać PATCH zamiast PUT?

PUT zwykle oznacza zastąpienie całego zasobu, a PATCH częściową aktualizację. Najważniejsze jest, żeby semantyka była spójna w całym API.

Czy GraphQL jest lepszy od REST?

Nie zawsze. GraphQL pomaga przy złożonych grafach danych i wielu klientach o różnych potrzebach, ale REST bywa prostszy, lepiej cachowalny i łatwiejszy do utrzymania.

Czym jest REST API i jak działa?

REST (Representational State Transfer) to styl architektoniczny dla API opartych na HTTP. Klient wysyła request do serwera wskazując zasób (URL) i operację (metoda HTTP), serwer zwraca reprezentację zasobu (najczęściej JSON). REST nie jest protokołem — to zbiór konwencji: zasoby jako rzeczowniki w URL, standaryzowane metody HTTP, odpowiednie kody statusu. Większość API w praktyce to "REST-like" HTTP API, nie idealnie zgodne z akademicką definicją.

Jaka jest różnica między PUT a PATCH?

PUT zamienia cały zasób — wysyłasz pełną reprezentację zasobu, serwer go zastępuje. Brakujące pola mogą być wyzerowane lub usunięte. PATCH aktualizuje tylko przekazane pola — wysyłasz tylko to, co chcesz zmienić, reszta pozostaje bez zmian. W praktyce: PATCH jest wygodniejszy do cząstkowych aktualizacji (zmiana emaila, statusu), PUT przy zastąpieniu całego obiektu.

Jaka jest różnica między 401 a 403?

401 Unauthorized — brak autentykacji: użytkownik nie jest zalogowany, token jest nieważny lub wygasł. Nazwa jest myląca — w praktyce chodzi o autentykację, nie autoryzację. 403 Forbidden — brak uprawnień: użytkownik jest zaautentykowany, ale nie ma prawa do tej operacji (np. zwykły user próbuje usunąć cudzego posta). Prosta reguła: 401 = "zaloguj się", 403 = "nie wolno Ci".

REST vs GraphQL — co wybrać?

REST jest prostszy w implementacji, lepiej cachuje (GET do CDN), jest bardziej przewidywalny i ma szerokie wsparcie narzędzi. GraphQL daje klientowi kontrolę nad kształtem danych (mniej over/under-fetchingu), lepiej sprawdza się przy złożonych, połączonych danych i wielu frontendach o różnych potrzebach. Dla większości aplikacji REST jest wystarczający i prostszy w utrzymaniu. GraphQL warto rozważyć przy publicznym API z wieloma konsumentami lub dużej złożoności grafu danych.

Jak wersjonować REST API?

Najpopularniejsza i najbardziej widoczna metoda to wersjonowanie w URL: /api/v1/users, /api/v2/users. Alternatywy to wersjonowanie przez nagłówek Accept: application/vnd.app.v2+json lub query parameter ?version=2. Praktyczna zasada: zacznij od /v1 już przy pierwszym publicznym release'ie — dodanie wersjonowania do istniejącego, bezwersyjnego API jest samo w sobie breaking change dla wszystkich jego konsumentów.

Co to jest idempotencja i dlaczego ważna w API?

Idempotentna operacja to taka, którą można wywołać wielokrotnie z tym samym skutkiem, czyli GET, PUT, DELETE są idempotentne, a ich kilkukrotne wywołanie nie zmienia efektu. POST nie jest idempotentny, ponieważ każde wywołanie tworzy nowy zasób. Praktyczne zastosowanie jest takie, że przy timeoutach i retriable requestach idempotency key (unikalny identyfikator requestu w nagłówku) pozwala serwerowi rozpoznać duplikat i nie wykonać operacji drugi raz, co jest kluczowe przy płatnościach.

Jak obsługiwać błędy w REST API?

Dobry format błędu zawiera: kod HTTP (4xx dla błędów klienta, 5xx dla serwera), maszynoczytelny kod błędu (np. VALIDATION_ERROR, NOT_FOUND), komunikat dla dewelopera i opcjonalnie szczegóły (lista błędów walidacji z polami). Unikaj zwracania 200 OK z błędem w body, ponieważ to utrudnia obsługę i cache. Bądź przy tym konsekwentny w formacie błędów przez całe API, bo jak pokazuje doświadczenie, to częsty problem.

O autorze

Maciej Sala

Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwijam własne projekty.

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Skontaktuj się ze mną

Biblioteka wiedzy na temat Backend

Czytaj dalej

Zobacz więcej wpisów
REST API WordPressa — integracja z React i Next.js

WordPress to nie tylko PHP i szablony, ponieważ od wersji 4.7 ma wbudowane REST API , dzięki któremu może funkcjonować jako headless CMS . Backend w WordPressie, frontend w React lub Next.js to w wielu projektach bardzo rozsądne rozwiązanie.

Maciej Sala

Maciej Sala

Founder StriveLab

Backend dla frontendowca: serwer, bazy danych i API

Frontend rzadko kończy się na komponencie i jednym fetch , a im bliżej realnego produktu, tym częściej okazuje się, że jakość UI zależy od tego, co dzieje się po drugiej stronie API . Jak backend paginuje dane, jak zwraca błędy, jak trzyma sesję, co robi przy timeoutach i czy potrafi przyjąć większy ruch.

Maciej Sala

Maciej Sala

Founder StriveLab

Route Handlers czy Server Actions? Kiedy co wybrać

App Router daje Ci dwa sposoby na uruchomienie kodu po stronie serwera, poprzez Route Handlers i Server Actions . Może wyglądają podobnie, ponieważ oba działają na serwerze, sięgają do bazy i do zmiennych środowiskowych, ale każdy z nich rozwiązuje zupełnie inny problem. Użycie jednego tam, gdzie pasuje drugi, nie jest odpowiednim rozwiązaniem, ponieważ zły wybór odbija się potem na architekturze.

Maciej Sala

Maciej Sala

Founder StriveLab