REST API to kelner w restauracji
Nie idziesz do kuchni. Mówisz kelnerowi, co chcesz - on wie, kogo spytać.
Idziesz do restauracji #
Siadasz przy stoliku. Chcesz spaghetti. Czy wchodzisz do kuchni i sam sobie gotujesz? Nie. Ochroniarz by Cię wyrzucił.
Zamiast tego mówisz kelnerowi: „Poproszę spaghetti carbonara”. Kelner idzie do kuchni, kucharz gotuje, kelner przynosi Ci talerz.
- Ty (klient) = przeglądarka, aplikacja mobilna.
- Kuchnia (serwer) = ta „czarna skrzynka”, gdzie siedzą dane.
- Kelner (API) = pośrednik. Zna menu i wie, co można, a czego nie.
Nie musisz wiedzieć, jak kucharz rozpalił piec. Wystarczy Ci, że dostałeś spaghetti. To jest cała idea API.
Menu = endpointy #
Każda restauracja ma menu. API też - nazywa się endpoint. Przykłady:
| Chcesz… | Co mówisz kelnerowi | Metoda HTTP |
|---|---|---|
| Zobaczyć listę dań | „Mogę prosić menu?” | GET /menu |
| Zamówić danie | „Poproszę pizzę” | POST /zamowienia |
| Zmienić zamówienie | „Bez cebuli proszę” | PATCH /zamowienia/42 |
| Anulować | „Jednak nie, dziękuję” | DELETE /zamowienia/42 |
To są czasowniki HTTP: GET, POST, PATCH, DELETE. Każdy ma swoją rolę. Kelner wie, co z nimi zrobić.
W kodzie #
// Klient (Ty, klient przy stoliku)
const odpowiedz = await fetch('https://api.restauracja.pl/menu');
const menu = await odpowiedz.json();
console.log(menu);
// { dania: ['pizza', 'pasta', 'sushi'], ceny: [35, 28, 42] }
Widzisz? Ani słowa o tym, jak kucharz robi pizzę. To tajemnica kuchni.
Bezstanowość - kelner ma krótką pamięć #
Kelner w restauracji REST nie pamięta, co zamówiłeś 5 minut temu. Za każdym razem musisz mu powiedzieć wszystko: „Jestem Szymon, stolik 4, proszę rachunek”. Kelner idzie, sprawdza, wraca.
Dlaczego? Bo jeśli kelner się rozchoruje, inny kelner może go zastąpić bez żadnej podpowiedzi. To daje systemom REST skalowalność - można mieć 100 kelnerów na raz.
Webhook - kelner sam przychodzi #
Normalnie pytasz: „Czy pizza gotowa?”. Co minutę pytasz. Męczące.
Webhook to kelner, który mówi: „Siedź spokojnie, sam do Ciebie podejdę, gdy pizza będzie gotowa”. I on przychodzi do Ciebie, nie odwrotnie.
W 2026 roku #
API to waluta internetu. Twój bank, Twój Uber, Twój Discord - wszystko to rozmowy z kelnerami. Jak zrozumiesz tę analogię, zrozumiesz architekturę wszystkich aplikacji, jakie dzisiaj używasz. Skalowalność jest jeszcze łatwiejsza, gdy kelner nie musi zaglądać do spiżarni - bo kuchnia siedzi w kontenerze jak gotowy lunchbox.
Frontend to fasada sklepu z cukierkami. Backend to potężne maszyny na zapleczu. API to drzwi między nimi.
REST jako styl architektoniczny #
REST nie jest protokołem. To zestaw ograniczeń opisanych przez Roya Fieldinga (2000). System który je spełnia dostaje konkretne własności: skalowalność, cache’owalność, prostotę ewolucji.
Sześć ograniczeń:
- Client-Server - separacja UI od storage.
- Stateless - każdy request zawiera cały kontekst, serwer nie trzyma sesji.
- Cacheable - odpowiedzi oznaczają czy wolno je cache’ować.
- Uniform interface - identyfikacja zasobów przez URI, manipulacja przez reprezentacje (JSON/XML), self-descriptive messages, HATEOAS.
- Layered system - klient nie wie czy gada z serwerem końcowym czy proxy.
- Code on demand (opcjonalne) - serwer może wysłać kod do wykonania.
W praktyce 99% “REST API” spełnia 4-5 z 6 (stateless + uniform interface to minimum). HATEOAS rzadko.
HTTP methods - semantyka #
| Metoda | Idempotent | Safe | Typowe użycie |
|---|---|---|---|
GET | tak | tak | odczyt zasobu, bez side effectów |
HEAD | tak | tak | metadata (Content-Length, Last-Modified) bez body |
POST | nie | nie | utworzenie zasobu, operacja bez naturalnego ID |
PUT | tak | nie | zastąpienie zasobu (pełne) |
PATCH | nie (zwykle) | nie | modyfikacja częściowa (JSON Patch / JSON Merge Patch) |
DELETE | tak | nie | usunięcie zasobu |
Idempotent = wielokrotne wywołanie daje ten sam efekt (klient może retry-ować bez skutków ubocznych). Safe = brak zmiany stanu serwera.
Status codes - używaj semantycznie #
| Kod | Znaczenie |
|---|---|
| 200 OK | sukces z body |
| 201 Created | zasób utworzony, Location: /orders/42 |
| 204 No Content | sukces bez body (DELETE, PUT) |
| 301 / 308 | permanent redirect |
| 400 Bad Request | klient wysłał niepoprawne dane |
| 401 Unauthorized | brak / zły token |
| 403 Forbidden | token OK, ale brak uprawnień |
| 404 Not Found | zasób nie istnieje |
| 409 Conflict | konflikt (np. unique constraint) |
| 422 Unprocessable Entity | walidacja biznesowa |
| 429 Too Many Requests | rate limit |
| 500 Internal Server Error | błąd po stronie serwera |
| 503 Service Unavailable | chwilowa niedostępność |
Anti-pattern: 200 OK z {"success": false, "error": ...} w body. Używaj kodów statusu zgodnie z ich znaczeniem - pośrednicy (proxy, monitoring, retry) zależą od tego.
ASP.NET Core minimal API #
// title: Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddProblemDetails(); // RFC 7807 error format
var app = builder.Build();
app.MapGet("/orders/{id:int}", async (int id, IOrderRepository repo) =>
{
var order = await repo.GetAsync(id);
return order is null ? Results.NotFound() : Results.Ok(order);
})
.WithName("GetOrder")
.Produces<Order>()
.Produces(StatusCodes.Status404NotFound);
app.MapPost("/orders", async (CreateOrderRequest req, IOrderRepository repo) =>
{
var id = await repo.CreateAsync(req);
return Results.CreatedAtRoute("GetOrder", new { id }, req);
})
.WithName("CreateOrder");
app.Run();
Uwagi:
- Binding z path / query / body dzieje się automatycznie.
Results.*zwracaIResult- typesafe mapowanie na status code + body.WithNameustawia ID operacji (używane przezCreatedAtRoute, OpenAPI).
Idempotency keys #
POST nie jest idempotentny, ale pewne operacje (płatności, utworzenie zasobu) muszą być bezpieczne do retry. Wzorzec: klient generuje Idempotency-Key (UUID), serwer zapisuje (key, response) w cache. Drugi request z tym samym key zwraca cached response.
app.MapPost("/payments", async (HttpContext ctx, PaymentRequest req, IIdempotencyStore store) =>
{
var key = ctx.Request.Headers["Idempotency-Key"].ToString();
if (store.TryGet(key, out var cached)) return Results.Json(cached);
var result = await ProcessPayment(req);
store.Save(key, result, TimeSpan.FromHours(24));
return Results.Ok(result);
});
Wersjonowanie #
Trzy popularne strategie:
- URL path -
/v1/orders,/v2/orders. Proste, widoczne. - Accept header -
Accept: application/vnd.company.v2+json. Czystsze URI, trudniejsze testowanie przez curl. - Query param -
/orders?api-version=2. Rzadziej, kompromis.
W ASP.NET Core - pakiet Asp.Versioning.Http.
Breaking vs non-breaking:
- Non-breaking: dodanie opcjonalnego pola, dodanie endpointu, rozszerzenie enum.
- Breaking: usunięcie pola, zmiana typu, zmiana semantyki error responses.
Zasada: nigdy nie usuwaj pola bez deprecation cycle (6-12 miesięcy), nawet jeśli “nikt tego nie używa”.
Paginacja #
- Offset-based -
?page=3&size=20. Prosto, ale wolne dla dużych offsetów (SQLOFFSETskanuje pominięte wiersze) i niestabilne przy insertach. - Cursor-based -
?after=eyJpZCI6MTAwfQ==. Stabilne, O(log n), nie pozwala na “skok na stronę 500”.
Dla logów, feedów - cursor. Dla tabel administracyjnych - offset bywa OK.
OpenAPI / Swagger #
Dokumentacja machine-readable. W ASP.NET Core od .NET 9 wbudowane przez Microsoft.AspNetCore.OpenApi:
builder.Services.AddOpenApi();
app.MapOpenApi(); // /openapi/v1.json
OpenAPI spec daje: contract testing, generację klientów (NSwag, openapi-generator), UI do eksploracji (Swagger UI, Scalar).
Error format - RFC 7807 Problem Details #
{
"type": "https://api.example.com/errors/out-of-stock",
"title": "Out of stock",
"status": 409,
"detail": "Product SKU-42 has insufficient inventory.",
"instance": "/orders/5123",
"available": 0
}
Standardowy format, rozszerzalny. ASP.NET Core ma builtin (AddProblemDetails + IProblemDetailsService).
Pułapki #
- Chunky APIs - zbyt wiele round-tripów (klient pobiera user, potem orders, potem items). Rozwiązanie: projekcja (
/users/42?include=orders), GraphQL, sparse fieldsets. - Overfetching - 100kB JSON kiedy klient chce 3 pola. Projekcja / pagination / field selection.
- Chatty auth - weryfikacja tokenu w każdym serwisie osobno, synchronicznie. Rozwiązanie: validate lokalnie (JWKS cache), centralizuj tylko issuance.
- Breaking changes bez wersji - jeden deployment psuje wszystkich klientów. Wersjonuj od pierwszego dnia.
Skrótowo #
REST to nie JSON nad HTTP - to statelessness, uniform interface, proper HTTP semantics. Używaj metod i statusów zgodnie z ich znaczeniem, wersjonuj od początku, dokumentuj przez OpenAPI, błędy zwracaj w Problem Details. W ASP.NET Core minimal API - kilka linii, cała semantyka działa z pudełka.
Podobne wpisy
Bazy wektorowe - biblioteka ułożona po nastrojach
Książki o miśkach leżą obok siebie, choć tytuły zaczynają się na różne litery. Tak działa baza wektorowa.
Komputer to bardzo szybki, ale całkowicie dosłowny wykonawca
Dlaczego kod musi być pisany z dokładnością, której nigdy nie żądamy od człowieka - i co z tego wynika dla całej branży.
Zero Trust - zamek, w którym każdy pokój ma strażnika
W starym modelu broniliśmy fosy. Dziś każde drzwi w zamku mają własną kontrolę.
🔗 Linkują tu