Wszystkie wpisy

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.

Każda restauracja ma menu. API też - nazywa się endpoint. Przykłady:

Chcesz…Co mówisz kelnerowiMetoda 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ń:

  1. Client-Server - separacja UI od storage.
  2. Stateless - każdy request zawiera cały kontekst, serwer nie trzyma sesji.
  3. Cacheable - odpowiedzi oznaczają czy wolno je cache’ować.
  4. Uniform interface - identyfikacja zasobów przez URI, manipulacja przez reprezentacje (JSON/XML), self-descriptive messages, HATEOAS.
  5. Layered system - klient nie wie czy gada z serwerem końcowym czy proxy.
  6. 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 #

MetodaIdempotentSafeTypowe użycie
GETtaktakodczyt zasobu, bez side effectów
HEADtaktakmetadata (Content-Length, Last-Modified) bez body
POSTnienieutworzenie zasobu, operacja bez naturalnego ID
PUTtakniezastąpienie zasobu (pełne)
PATCHnie (zwykle)niemodyfikacja częściowa (JSON Patch / JSON Merge Patch)
DELETEtaknieusunię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 #

KodZnaczenie
200 OKsukces z body
201 Createdzasób utworzony, Location: /orders/42
204 No Contentsukces bez body (DELETE, PUT)
301 / 308permanent redirect
400 Bad Requestklient wysłał niepoprawne dane
401 Unauthorizedbrak / zły token
403 Forbiddentoken OK, ale brak uprawnień
404 Not Foundzasób nie istnieje
409 Conflictkonflikt (np. unique constraint)
422 Unprocessable Entitywalidacja biznesowa
429 Too Many Requestsrate limit
500 Internal Server Errorbłąd po stronie serwera
503 Service Unavailablechwilowa 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.* zwraca IResult - typesafe mapowanie na status code + body.
  • WithName ustawia ID operacji (używane przez CreatedAtRoute, 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:

  1. URL path - /v1/orders, /v2/orders. Proste, widoczne.
  2. Accept header - Accept: application/vnd.company.v2+json. Czystsze URI, trudniejsze testowanie przez curl.
  3. 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 (SQL OFFSET skanuje 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

🔗 Linkują tu