Co to jest REST API?
REST (Representational State Transfer) to styl architektoniczny wykorzystywany do projektowania systemów rozproszonych, w szczególności interfejsów API umożliwiających komunikację pomiędzy aplikacjami. Został opisany w 2000 roku przez Roya Fieldinga w jego rozprawie doktorskiej na Uniwersytecie Kalifornijskim w Irvine.
REST nie jest protokołem ani technologią, lecz zbiorem zasad architektonicznych określających, w jaki sposób powinny komunikować się ze sobą komponenty systemu.
REST API to z koleji interfejs programistyczny zaprojektowany, aby umożlwić wymianę informacji oraz wykonywanie operacji na zasobach udostępnianych przez serwer. W praktyce REST API najczęściej wykorzystuje protokół HTTP, a dane przesyłane są w formacie JSON.
Podstawowe zasady architektury REST
Architektura REST opiera się na kilku fundamentalnych zasadach:
- Client–Server – rozdzielenie odpowiedzialności pomiędzy klientem a serwerem. Klient odpowiada za interfejs użytkownika, natomiast serwer za przetwarzanie żądań i udostępnianie zasobów.
- Stateless (bezstanowość) – każde żądanie powinno zawierać wszystkie informacje niezbędne do jego obsłużenia. Serwer nie przechowuje kontekstu sesji klienta pomiędzy żądaniami.
- Cacheable (buforowanie) – odpowiedzi serwera powinny określać, czy mogą być przechowywane w pamięci podręcznej, co pozwala ograniczyć liczbę żądań i poprawić wydajność.
- Uniform Interface (jednolity interfejs) – komunikacja pomiędzy komponentami odbywa się według spójnych zasad, niezależnie od ich implementacji.
- Layered System (system warstwowy) – architektura może składać się z wielu warstw pośredniczących, takich jak serwery proxy, bramy API czy load balancery.
REST a HTTP?
REST a HTTP to dwie różne rzeczy. HTTP (Hypertext Transfer Protocol) to protokół komunikacyjny definiujący sposób przesyłania żądań i odpowiedzi pomiędzy klientem a serwerem. Natomiast REST określa zasady architektoniczne, według których taka komunikacja powinna być zaprojektowana.
Jak działa REST API?
Jak już wspomniałem komunikacja w REST API opiera się najczęściej na modelu klient–serwer. Klient wysyła żądanie (request) HTTP do serwera, który przetwarza je, wykonuje odpowiednią operację i zwraca odpowiedź (response). Klientem może być aplikacja internetowa, aplikacja mobilna lub inny system. Serwer natomiast udostępnia zasoby i odpowiada za ich przetwarzanie.
Mechanizm Request–Response
Komunikację z REST API można przedstawić na przykładzie pobierania informacji o produkcie ze sklepu internetowego.
1. Klient wysyła żądanie HTTP
GET /api/products/10 HTTP/1.1 Host: api.example.com Accept: application/json
W żądaniu znajdują się:
- Metoda HTTP (GET) – określa rodzaj operacji, którą klient chce wykonać.
- URI (/api/products/10) – wskazuje zasób, którego dotyczy żądanie.
- Nagłówki (Headers) – przekazują dodatkowe informacje, np. oczekiwany format odpowiedzi.
2. Serwer przetwarza żądanie
3. Serwer zwraca odpowiedź HTTP
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 10,
"name": "Laptop",
"price": 3500.00,
"currency": "PLN"
}Odpowiedź zawiera:
- Status HTTP – informuje o wyniku operacji, w tym przypadku jest to status OK (o numerze 200).
- Nagłówki odpowiedzi – określają m.in. format zwracanych danych.
- Response Body – zawiera reprezentację zasobu, w tym przypadku w formacie JSON.
Jeżeli produkt o wskazanym identyfikatorze nie istnieje, serwer może zwrócić inna odpowiedź (np. o błędzie).
Bezstanowość (Stateless)
Jedną z najważniejszych cech REST jest bezstanowość komunikacji. Oznacza to, że każde żądanie klienta powinno zawierać wszystkie informacje niezbędne do jego obsłużenia, bez konieczności odwoływania się do kontekstu poprzednich żądań.
Metody HTTP w REST API
Protokół HTTP udostępnia zestaw metod określających rodzaj operacji, jaką klient może wykonać na zasobie serwera. W REST API najczęściej wykorzystuje się pięć podstawowych metod: GET, POST, PUT, PATCH oraz DELETE. Postaram się teraz je tutaj omówić.
GET – pobieranie danych
Metoda GET służy do pobierania reprezentacji zasobów z serwera. Nie powinna powodować zmian ich stanu.
Przykładowo, aby pobrać listę produktów ze sklepu internetowego, można wykonać żądanie:
GET /api/products
Natomiast pobranie konkretnego produktu o identyfikatorze 10 będzie wyglądało następująco:
GET /api/products/10
Serwer zwróci odpowiedź zawierającą dane produktu, zazwyczaj w formacie JSON.
Metoda GET jest bezpieczna (Safe) oraz idempotentna (Idempotent). Oznacza to, że jej wywołanie nie powinno modyfikować zasobu, a wielokrotne wykonanie identycznego żądania ma taki sam zamierzony efekt jak pojedyncze wywołanie.
POST – tworzenie zasobów
Metoda POST służy do przesyłania danych do serwera w celu ich przetworzenia. W REST API najczęściej wykorzystuje się ją do tworzenia nowych zasobów.
Przykład dodania produktu:
POST /api/products
Content-Type: application/json
{
"name": "Laptop",
"price": 3500.00
}Serwer przetwarza przesłane dane i tworzy nowy produkt. W przypadku powodzenia powinien zazwyczaj zwrócić status 201 Created oraz może wskazać adres utworzonego zasobu za pomocą nagłówka Location.
HTTP/1.1 201 Created Location: /api/products/10
Metoda POST nie jest domyślnie idempotentna. Dwukrotne wysłanie identycznego żądania może przykładowo spowodować utworzenie dwóch różnych produktów.
PUT – zastępowanie zasobów
Metoda PUT służy do utworzenia lub zastąpienia stanu zasobu pod wskazanym adresem URI.
Przykładowo, aby zastąpić dane produktu o identyfikatorze 10, można wysłać żądanie:
PUT /api/products/10
Content-Type: application/json
{
"name": "Laptop Dell",
"price": 4000.00
}W odróżnieniu od PATCH, PUT służy do przekazania docelowego stanu zasobu, a nie jedynie instrukcji zmiany wybranych pól. W praktyce oznacza to, że pominięcie niektórych właściwości może skutkować ich usunięciem, przywróceniem wartości domyślnych lub odrzuceniem żądania, zależnie od kontraktu API.
PUT jest metodą idempotentną. Wielokrotne wykonanie identycznego żądania powinno prowadzić do tego samego docelowego stanu zasobu.
PATCH – częściowa aktualizacja zasobów
Metoda PATCH służy do wprowadzania częściowych zmian w istniejącym zasobie.
Przykładowe żądanie wykorzystujące format JSON Merge Patch:
PATCH /api/products/10
Content-Type: application/merge-patch+json
{
"price": 4200.00
}W tym przypadku zmodyfikowana zostanie jedynie cena produktu.
Najważniejsza różnica między PUT a PATCH polega na tym, że PUT określa docelową reprezentację zasobu, natomiast PATCH opisuje zmiany, które należy w nim wprowadzić.
PATCH nie jest z definicji idempotentny, chociaż konkretna operacja może spełniać tę właściwość.
Przykładowo, ustawienie ceny produktu na 4200 PLN jest idempotentne, natomiast operacja polegająca na zwiększeniu ceny o 100 PLN przy każdym wywołaniu już nie.
DELETE – usuwanie zasobów
Metoda DELETE służy do usuwania powiązania wskazanego zasobu z jego adresem URI. W praktycznych REST API najczęściej oznacza usunięcie zasobu.
Przykładowo:
DELETE /api/products/10
Serwer usuwa wskazany produkt i może zwrócić odpowiedź:
HTTP/1.1 204 No Content
Metoda DELETE jest idempotentna. Usunięcie tego samego zasobu kilkukrotnie powinno mieć taki sam końcowy efekt jak pojedyncze usunięcie.
Nie oznacza to jednak, że każde wywołanie musi zwracać identyczny kod HTTP. Pierwsze żądanie może zakończyć się statusem 204 No Content, a kolejne 404 Not Found, ponieważ zasób już nie istnieje.
Kody odpowiedzi HTTP
Każde żądanie HTTP wysłane do serwera kończy się odpowiedzią zawierającą kod statusu HTTP (HTTP Status Code). Jest to trzycyfrowa liczba informująca klienta o wyniku przetwarzania żądania.
Kody odpowiedzi HTTP dzielą się na pięć podstawowych kategorii:
| Kategoria | Znaczenie |
|---|---|
| 1xx | Informacje o przebiegu komunikacji |
| 2xx | Żądanie zakończone sukcesem |
| 3xx | Przekierowania i inne działania wymagane do obsługi żądania |
| 4xx | Błędy związane z żądaniem klienta |
| 5xx | Błędy po stronie serwera |
Poprawne wykorzystanie kodów HTTP jest jednym z fundamentów projektowania REST API. Dzięki nim klient może rozpoznać, czy operacja zakończyła się powodzeniem, czy wystąpił błąd oraz jak powinien na niego zareagować.
1xx – odpowiedzi informacyjne
Kody z grupy 1xx oznaczają, że serwer otrzymał żądanie, ale komunikacja nie została jeszcze zakończona. Są to odpowiedzi tymczasowe, po których zazwyczaj następuje odpowiedź końcowa.
Najważniejsze przykłady:
- 100 Continue – serwer informuje klienta, że może kontynuować przesyłanie treści żądania.
- 101 Switching Protocols – serwer potwierdza zmianę protokołu komunikacyjnego, np. podczas przechodzenia z HTTP/1.1 do WebSocket
W codziennej pracy z REST API programista stosunkowo rzadko musi bezpośrednio obsługiwać te kody.
2xx – poprawne wykonanie żądania
Kody z grupy 2xx informują, że żądanie zostało pomyślnie obsłużone.
Najczęściej wykorzystywane statusy to:
| Kod | Nazwa | Zastosowanie |
|---|---|---|
| 200 | OK | Operacja zakończona sukcesem, np. pobranie produktu |
| 201 | Created | Utworzono nowy zasób |
| 202 | Accepted | Przyjęto żądanie do przetwarzania, ale operacja nie została jeszcze zakończona |
| 204 | No Content | Operacja zakończona sukcesem, bez zwracania treści odpowiedzi |
Przykładowo, pobranie produktu za pomocą metody GET może zakończyć się odpowiedzią:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 10,
"name": "Laptop",
"price": 3500.00
}Z kolei utworzenie nowego produktu za pomocą metody POST powinno zazwyczaj zwrócić status 201 Created wraz z adresem utworzonego zasobu:
HTTP/1.1 201 Created Location: /api/products/10
W przypadku usuwania produktu często wykorzystuje się 204 No Content, ponieważ klient nie musi otrzymywać dodatkowych danych.
Warto również zwrócić uwagę na 202 Accepted, który jest szczególnie przydatny w systemach wykorzystujących komunikację asynchroniczną. Przykładowo API może przyjąć żądanie wygenerowania raportu, przekazać zadanie do kolejki i natychmiast zwrócić status 202, nie czekając na zakończenie generowania.
3xx – przekierowania
Kody z grupy 3xx informują klienta, że do obsłużenia żądania konieczne może być wykonanie dodatkowej czynności, najczęściej skorzystanie z innego adresu URL.
Najważniejsze przykłady:
- 301 Moved Permanently – zasób został trwale przeniesiony pod inny adres.
- 302 Found – zasób jest tymczasowo dostępny pod innym adresem.
- 304 Not Modified – reprezentacja zasobu nie została zmodyfikowana względem wersji wskazanej przez klienta, dzięki czemu może on wykorzystać wcześniej pobrane dane z pamięci podręcznej.
4xx – błędy klienta
Kody z grupy 4xx oznaczają, że serwer nie może zrealizować żądania z przyczyn związanych z samym żądaniem lub uprawnieniami klienta.
| Kod | Nazwa | Zastosowanie |
|---|---|---|
| 400 | Bad Request | Nieprawidłowe żądanie, np. błędna składnia JSON |
| 401 | Unauthorized | Brak poprawnego uwierzytelnienia |
| 403 | Forbidden | Brak uprawnień do wykonania operacji |
| 404 | Not Found | Nie znaleziono wskazanego zasobu |
| 405 | Method Not Allowed | Metoda HTTP nie jest obsługiwana dla danego zasobu |
| 409 | Conflict | Konflikt z aktualnym stanem zasobu |
| 415 | Unsupported Media Type | Nieobsługiwany format przesłanych danych |
| 422 | Unprocessable Content | Poprawna składnia żądania, ale niemożliwe przetworzenie jego zawartości |
| 429 | Too Many Requests | Przekroczono dopuszczalny limit żądań |
Szczególnie istotne jest rozróżnienie pomiędzy 401 Unauthorized a 403 Forbidden.
403 Forbidden– serwer odmawia dostępu do zasobu, np. uwierzytelniony użytkownik nie ma odpowiednich uprawnień.
Warto również rozróżniać błędy 400 Bad Request i 422 Unprocessable Content. Pierwszy jest ogólnym statusem nieprawidłowego żądania, natomiast drugi pozwala precyzyjniej wskazać problem z przetworzeniem poprawnie sformułowanej treści.
Przykładowo, próba dodania produktu z ujemną ceną może zakończyć się odpowiedzią:
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Cena produktu nie może być ujemna."
}Przedstawiony format błędu jest zgodny z koncepcją Problem Details, zdefiniowaną w RFC 9457, która umożliwia ujednolicenie odpowiedzi błędów w HTTP API.
5xx – błędy serwera
Kody z grupy 5xx informują, że serwer nie był w stanie poprawnie obsłużyć żądania.
Najważniejsze przykłady:
| Kod | Nazwa | Zastosowanie |
|---|---|---|
| 500 | Internal Server Error | Nieoczekiwany błąd podczas przetwarzania żądania |
| 502 | Bad Gateway | Serwer pośredniczący otrzymał nieprawidłową odpowiedź od innego serwera |
| 503 | Service Unavailable | Usługa jest tymczasowo niedostępna |
| 504 | Gateway Timeout | Serwer pośredniczący nie otrzymał odpowiedzi od innej usługi w wymaganym czasie |
W architekturze mikroserwisowej szczególnie często spotyka się błędy 502 Bad Gateway, 503 Service Unavailable oraz 504 Gateway Timeout, ponieważ komunikacja pomiędzy usługami może przebiegać przez API Gateway, reverse proxy lub load balancer.
Przykładowo, jeżeli API Gateway nie otrzyma odpowiedzi od mikroserwisu odpowiedzialnego za zamówienia w określonym czasie, może zwrócić klientowi status 504 Gateway Timeout.
Warto pamiętać, że odpowiedź 500 Internal Server Error nie powinna zawierać szczegółowych informacji o implementacji aplikacji, takich jak stack trace, zapytania SQL czy dane konfiguracyjne.
Jak dobierać właściwe kody HTTP?
Jednym z częstych błędów podczas projektowania REST API jest zwracanie kodu 200 OK niezależnie od faktycznego wyniku operacji, a następnie umieszczanie informacji o błędzie wyłącznie w treści odpowiedzi.
Takie rozwiązanie jest nieprawidłowe z punktu widzenia semantyki HTTP, ponieważ kod 200 informuje o sukcesie, mimo że żądana operacja nie została zrealizowana.
Poprawniejszym rozwiązaniem będzie zwrócenie statusu 404 Not Found wraz z opcjonalnym opisem błędu.
Format danych i komunikacja w REST API
Komunikacja pomiędzy klientem a serwerem w REST API polega na wymianie żądań i odpowiedzi HTTP. Oprócz metod HTTP i kodów odpowiedzi istotną rolę odgrywają format przesyłanych danych, nagłówki HTTP (Headers) oraz treść wiadomości (Body).
REST nie narzuca konkretnego formatu danych. W praktyce najczęściej wykorzystywany jest JSON, ale możliwe są też inne formaty danych: XML, HTML czy jako zwykły tekst
JSON (JavaScript Object Notation) to lekki, tekstowy format wymiany danych, który jest czytelny zarówno dla człowieka, jak i dla maszyny. Wbrew nazwie nie dotyczy on tylko i wyłącznie JavaScript.
Przykład:
{
"id": 10,
"name": "Laptop Dell",
"price": 3500.00,
"available": true,
"categories": ["Electronics", "Computers"],
"manufacturer": {
"id": 5,
"name": "Dell"
}
}JSON obsługuje następujące typy danych:
- String – ciągi znaków, np.
"Laptop Dell". - Number – liczby, np.
3500.00. - Boolean – wartości logiczne
trueifalse. - Null – brak wartości.
- Array – tablice elementów, np.
["Electronics", "Computers"]. - Object – obiekty zawierające pary klucz–wartość.
Request Body i Response Body
Treść wiadomości HTTP (Body) służy do przesyłania danych pomiędzy klientem a serwerem.
Request Body to dane przesyłane przez klienta do serwera. Najczęściej występują w żądaniach POST, PUT i PATCH, np. podczas tworzenia lub aktualizowania zasobu.
Response Body to dane zwracane przez serwer w odpowiedzi na żądanie klienta.
Nie każda odpowiedźmusi zawierać Response Body. Zależy to od logiki zapytania.
Nagłówki HTTP służą do przekazywania dodatkowych informacji dotyczących żądania lub odpowiedzi. Pozwalają określić między innymi format danych, sposób uwierzytelniania, zasady buforowania oraz preferencje klienta.
Najczęściej spotykane:
| Nagłówek | Zastosowanie |
|---|---|
Content-Type | Określa format przesyłanej treści |
Accept | Określa format odpowiedzi akceptowany przez klienta |
Authorization | Przekazuje dane uwierzytelniające, np. token |
Cache-Control | Określa zasady buforowania odpowiedzi |
Location | Wskazuje adres zasobu, np. po jego utworzeniu |
ETag | Identyfikuje konkretną wersję reprezentacji zasobu |
If-None-Match | Umożliwia warunkowe pobieranie danych na podstawie ETag |
Dobre praktyki projektowania REST API
Poniżej przedstawiam najważniejsze dobre praktyki, które warto stosować podczas projektowania REST API.
1. Stosuj czytelne nazewnictwo endpointów
Adresy URI powinny jednoznacznie identyfikować zasoby. Dobrą praktyką jest wykorzystywanie rzeczowników zamiast czasowników, ponieważ rodzaj wykonywanej operacji określa już metoda HTTP.
Przykładowo:
| Niepoprawne | Zalecane |
|---|---|
GET /getProducts | GET /products |
GET /getProduct/10 | GET /products/10 |
POST /createProduct | POST /products |
DELETE /deleteProduct/10 | DELETE /products/10 |
GET /getUserOrders/5 | GET /users/5/orders |
Każda metoda HTTP ma określoną semantykę, dlatego należy wykorzystywać ją zgodnie z przeznaczeniem.
Przykładowo, nie należy usuwać produktu za pomocą żądania GET:
GET /products/10/delete
Poprawnym rozwiązaniem jest:
DELETE /products/10
Podobnie podczas tworzenia zasobu należy zazwyczaj zwrócić kod 201 Created, a w przypadku próby pobrania nieistniejącego produktu — 404 Not Found.
2. Poprawnie wykorzystuj metody i kody HTTP
Każda metoda HTTP ma określoną semantykę, dlatego należy wykorzystywać ją zgodnie z przeznaczeniem.
Przykładowo, nie należy usuwać produktu za pomocą żądania GET:
GET /products/10/delete
Poprawnym rozwiązaniem jest:
DELETE /products/10
3. Stosuj filtrowanie, sortowanie i paginację
Endpoint zwracający kolekcję zasobów nie powinien bez ograniczeń pobierać wszystkich rekordów z bazy danych. W przypadku dużej liczby danych może to prowadzić do problemów z wydajnością oraz nadmiernego zużycia pamięci.
Dlatego warto stosować trzy mechanizmy:
Filtrowanie (Filtering) umożliwia pobieranie zasobów spełniających określone kryteria:
GET /products?category=electronics
Sortowanie (Sorting) pozwala określić kolejność zwracanych danych:
GET /products?sort=price,asc
Paginacja (Pagination) umożliwia pobieranie danych w mniejszych partiach:
GET /products?page=0&size=20
4. Wersjonuj REST API
Wraz z rozwojem aplikacji mogą pojawić się zmiany w strukturze zasobów, formacie odpowiedzi czy sposobie działania endpointów.
W przypadku zmian niekompatybilnych z dotychczasowym kontraktem API warto wprowadzić wersjonowanie, aby nie zakłócić działania istniejących klientów.
Jednym z najprostszych rozwiązań jest wersjonowanie w adresie URI:
GET /api/v1/products GET /api/v2/products
struktury odpowiedzi.
5. Zadbaj o spójną obsługę błędów
REST API powinno zwracać nie tylko odpowiednie kody HTTP, ale również czytelne informacje umożliwiające klientowi zrozumienie przyczyny błędu.
Dobrym rozwiązaniem jest wykorzystanie standardu Problem Details for HTTP APIs (RFC 9457).
Przykładowa odpowiedź dotycząca błędu walidacji:
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation error",
"status": 422,
"detail": "Product data is invalid",
"errors": [
{
"field": "price",
"message": "Price must be greater than zero"
}
]
}Pole errors jest w tym przykładzie rozszerzeniem standardowego formatu Problem Details.
6. Zadbaj o bezpieczeństwo API
REST API często udostępnia dane użytkowników oraz operacje biznesowe, dlatego jego bezpieczeństwo powinno być uwzględnione już na etapie projektowania.
Najważniejsze praktyki obejmują:
- HTTPS – szyfrowanie komunikacji pomiędzy klientem a serwerem.
- Uwierzytelnianie (Authentication) – weryfikację tożsamości klienta, np. za pomocą tokenu OAuth 2.0.
- Walidację danych wejściowych – kontrolę poprawności danych przesyłanych do API.
- Rate Limiting – ograniczanie liczby żądań wykonywanych przez klienta w określonym czasie.
- Ochronę danych wrażliwych – unikanie przekazywania haseł, tokenów oraz innych sekretów w adresach URL.
7. Dokumentuj REST API
Dobrze zaprojektowane API powinno posiadać aktualną dokumentację opisującą dostępne endpointy, parametry żądań, formaty odpowiedzi oraz możliwe błędy.
Jednym z najpopularniejszych standardów jest OpenAPI Specification (OAS), umożliwiający opisanie kontraktu API w formacie JSON lub YAML.
Do prezentacji dokumentacji można wykorzystać Swagger UI, który umożliwia również interaktywne wykonywanie żądań.
Podsumowanie
REST API to jedno z najpopularniejszych rozwiązań wykorzystywanych do komunikacji pomiędzy aplikacjami, szczególnie w architekturze mikroserwisowej oraz systemach webowych. Dzięki wykorzystaniu standardowych mechanizmów protokołu HTTP umożliwia tworzenie prostych, elastycznych i łatwych w integracji interfejsów.
W artykule omówiłem podstawowe zasady architektury REST, metody HTTP, kody odpowiedzi oraz sposoby wymiany danych pomiędzy klientem a serwerem. Przedstawiłem również dobre praktyki projektowania API i prosty przykład implementacji CRUD w Java i Spring Boot.
Warto pamiętać, że dobrze zaprojektowane REST API to nie tylko zestaw endpointów zwracających JSON. Kluczowe znaczenie mają odpowiednie wykorzystanie metod i kodów HTTP, bezstanowość komunikacji, spójne nazewnictwo zasobów oraz prawidłowa obsługa błędów.
REST nie jest rozwiązaniem idealnym dla każdego przypadku użycia, jednak dzięki swojej prostocie, szerokiemu wsparciu technologicznemu i wykorzystaniu standardów HTTP pozostaje jednym z podstawowych podejść do projektowania API we współczesnych aplikacjach.
