REST w 2026 r.: najlepsze praktyki, wersjonowanie i model dojrzałości Richardsona
Większość interfejsów API, które nazywają siebie „RESTful”, taka nie jest. Używają protokołu HTTP transport, JSON jako format i na tym poprzestań. The Model dojrzałości Richardsona, wprowadzony przez Leonarda Richardsona w 2008 roku i spopularyzowany przez Martina Fowlera, zapewnia skalować na czterech poziomach, co odróżnia ogólne API HTTP (poziom 0) od prawdziwego API RESTful (poziom 3 z HATEOAS).
Perfekcjonizm architektoniczny nie jest jednak celem tego przewodnika. Celem jest nauczenie Cię do tworzenia interfejsów API REST, które są prawidłowe, łatwe w utrzymaniu i dobrze udokumentowane: stosuj odpowiednią semantykę HTTP, wdrażaj z czasem zrównoważone wersjonowanie, zarządzaj skutecznie buforuj za pomocą ETag i dokumentuj umowę za pomocą OpenAPI 3.1. To są rzeczywiste różnice między profesjonalnym API a takim, który sprawia problemy konsumentom.
Czego się nauczysz
- 4 poziomy modelu dojrzałości Richardsona z konkretnymi przykładami
- Poprawna semantyka HTTP: metody, kody stanu, nagłówki
- Idempotencja i metody bezpieczne: podstawy teoretyczne z zastosowaniami praktycznymi
- Strategie wersjonowania: URI, nagłówek i wersjonowanie poprzez zmiany addytywne
- Żądania ETag i warunkowe zapewniające wydajne buforowanie
- OpenAPI 3.1: struktura dokumentu i najlepsze praktyki
Model dojrzałości Richardsona: 4 poziomy
Model dojrzałości Richardsona mierzy „RESTfulness” interfejsu API w skali 0 do 3. Zrozumienie tych poziomów pomaga zidentyfikować luki architektoniczne do istniejących interfejsów API i budować nowe na odpowiednim poziomie.
Poziom 0: HTTP jako transport (tunelowanie)
// Livello 0: tutto su un unico endpoint, azione nel body
POST /api
Content-Type: application/json
{
"action": "getUser",
"id": 123
}
POST /api
{
"action": "createUser",
"name": "Federico",
"email": "federico@example.com"
}
// HTTP e solo un canale: la semantica e tutta nell'applicazione
// Nessun caching possibile, nessuna semantica uniforme
Poziom 1: Zasoby (znaczący identyfikator URI)
// Livello 1: URL per risorse, ma ancora solo POST
POST /users/123 // Non ha senso: POST per leggere?
POST /users/create
POST /users/delete/123
POST /users/get/all
// Migliore, ma i verbi HTTP non sono usati semanticamente
Poziom 2: Czasowniki HTTP (poziom standardowy)
// Livello 2: URL meaningful + verbi HTTP corretti + status codes
GET /users -> 200 OK con lista
GET /users/123 -> 200 OK con utente, 404 Not Found
POST /users -> 201 Created con Location header
PUT /users/123 -> 200 OK aggiornato, 404 Not Found
PATCH /users/123 -> 200 OK parzialmente aggiornato
DELETE /users/123 -> 204 No Content, 404 Not Found
// Questo e il livello che la maggior parte delle API raggiunge
// ed e generalmente sufficiente per la produzione
Poziom 3: HATEOAS (Hypermedia jako silnik stanu aplikacji)
// Livello 3: ogni risposta contiene link alle azioni disponibili
GET /users/123
-> 200 OK
{
"id": 123,
"name": "Federico",
"email": "federico@example.com",
"status": "active",
"_links": {
"self": { "href": "/users/123" },
"orders": { "href": "/users/123/orders" },
"deactivate": { "href": "/users/123/deactivate", "method": "POST" },
"update": { "href": "/users/123", "method": "PUT" }
}
}
// Il client non deve "sapere" a priori gli URL: li scopre dalle risposte
// Permette di cambiare URL senza rompere i client (in teoria)
HATEOAS: Czy warto?
HATEOAS jest teoretycznie elegancki, ale rzadko wdrażany w praktyce. Powody:
- Znacząco zwiększa rozmiar odpowiedzi
- Jednak klienci często „kodują” adresy URL w celu zwiększenia wydajności
- Wymaga specjalnych narzędzi do poruszania się po linkach
- Większość zespołów używa zamiast tego OpenAPI jako kontraktu
Poziom 2 + OpenAPI 3.1 i pragmatyczny idealny punkt dla większości interfejsów API w 2026 r. HATEOAS ma sens głównie w publicznych interfejsach API, gdzie stabilność adresu URL jest długoterminowa termin i krytyka.
Popraw semantykę HTTP
Używanie czasowników HTTP z odpowiednią semantyką to nie tylko problem estetyczny: ma wpływ buforowanie, idempotencja i zdolność klientów do bezpiecznego wycofywania żądań.
// Proprietà dei metodi HTTP
Metodo | Safe | Idempotente | Body richiesta | Uso corretto
--------|------|-------------|----------------|----------------------------------
GET | SI | SI | No | Lettura, query, ricerca
HEAD | SI | SI | No | Verifica esistenza, metadata
OPTIONS | SI | SI | No | CORS preflight, capabilities
POST | NO | NO | SI | Creazione, azioni non-idempotenti
PUT | NO | SI | SI | Sostituzione completa di risorsa
PATCH | NO | NO* | SI | Modifica parziale
DELETE | NO | SI | Opzionale | Eliminazione
// *PATCH puo essere reso idempotente con patch semantics JSON Patch (RFC 6902)
Bezpieczna oznacza to, że żądanie nie ma skutków ubocznych po stronie serwera (serwer nie zmienia stanu). Klient może „nacisnąć klawisz F5” podczas operacji GET bez konsekwencji.
Idempotentny means that multiple identical requests produce the same wynikiem pojedynczego żądania. Kluczowe przy ponownych próbach: jeśli sieć ulegnie awarii podczas DELETE, klient może spróbować ponownie bez obawy, że usunie więcej niż raz.
Kody stanu: używaj właściwych
Nadużycie 200 OK za wszystko (w tym błędy w treści) i jeden z anty-wzorców
bardziej powszechne. Używanie prawidłowych kodów umożliwia klientom, proxy i narzędziom monitorującym
zinterpretuj poprawnie odpowiedzi:
// Status codes piu importanti con esempi di uso corretto
// 2xx: Success
200 OK - GET, PUT, PATCH con risorsa nel body
201 Created - POST che crea una risorsa (+ Location header)
202 Accepted - Operazione asincrona accettata (non ancora completata)
204 No Content - DELETE, PUT/PATCH senza body di risposta
// 3xx: Redirection
301 Moved Permanently - Redirect permanente (aggiornare i bookmark)
302 Found - Redirect temporaneo
304 Not Modified - ETag/If-None-Match: risorsa non cambiata, usa la cache
// 4xx: Client Error (il client ha sbagliato)
400 Bad Request - Input malformato, schema validation fallita
401 Unauthorized - Non autenticato (serve login)
403 Forbidden - Autenticato ma non autorizzato
404 Not Found - Risorsa non trovata
405 Method Not Allowed - Metodo HTTP non supportato su questo endpoint
409 Conflict - Conflitto di stato (es: email gia esistente)
410 Gone - Risorsa eliminata permanentemente (vs 404)
422 Unprocessable Entity - Sintassi ok ma semantica invalida
429 Too Many Requests - Rate limit raggiunto (+ Retry-After header)
// 5xx: Server Error (colpa del server)
500 Internal Server Error - Errore generico non gestito
502 Bad Gateway - Errore dal backend upstream
503 Service Unavailable - Server temporaneamente non disponibile
504 Gateway Timeout - Timeout dal backend upstream
PUT vs PATCH: ważne rozróżnienie
Pomylenie PUT i PATCH jest powszechne, ale ma konkretne implikacje:
// PUT: sostituzione COMPLETA della risorsa (idempotente)
// Se ometti un campo, viene azzerato!
PUT /users/123
{
"name": "Federico Calo",
"email": "federico@example.com"
// Se il campo "phone" non e incluso, viene rimosso!
}
// PATCH: modifica PARZIALE (solo i campi specificati)
PATCH /users/123
{
"name": "Federico Calo"
// Solo name viene aggiornato, email e phone rimangono invariati
}
// PATCH con JSON Patch (RFC 6902): piu preciso e idempotente
PATCH /users/123
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/name", "value": "Federico Calo" },
{ "op": "add", "path": "/phone", "value": "+39 333 1234567" },
{ "op": "remove", "path": "/tempNote" }
]
Strategie wersjonowania
Wersjonowanie jest jedną z najważniejszych decyzji przy projektowaniu publicznego API. Raz że klienci są zależni od API, zmiana umowy powoduje uszkodzenie ich aplikacji. Le Główne strategie wymagają różnych kompromisów:
1. Wersjonowanie URI (najczęściej)
GET /api/v1/users // Versione 1
GET /api/v2/users // Versione 2 con campi aggiuntivi
// Vantaggi:
// - Visibile e ovvio
// - Cacheable a livello HTTP (l'URL e diverso)
// - Facile da esplorare con il browser
// - Semplice da loggare e monitorare
// Svantaggi:
// - "Sporco" semanticamente (la versione non e parte della risorsa)
// - Proliferazione di URL nel tempo
2. Wersjonowanie nagłówka
GET /api/users
Accept: application/vnd.myapi.v2+json
// oppure
API-Version: 2
// Vantaggi:
// - URL "puliti"
// - Piu vicino alla semantica HTTP originale
// Svantaggi:
// - Non cacheable con HTTP standard (Cache-Vary header necessario)
// - Invisibile dalla URL (difficile da debuggare)
// - Meno intuitivo per i nuovi consumatori dell'API
3. Wersjonowanie poprzez zmiany addytywne (najlepsze)
// Strategia: non cambiare mai, solo aggiungere (non rompere mai i client)
// V1 risposta:
GET /api/users/123
{
"id": 123,
"name": "Federico",
"email": "federico@example.com"
}
// Aggiungi campi senza versione (i client vecchi ignorano i nuovi campi):
GET /api/users/123
{
"id": 123,
"name": "Federico",
"email": "federico@example.com",
"createdAt": "2025-01-15T10:30:00Z", // AGGIUNTO: non rompe i client vecchi
"avatarUrl": null // AGGIUNTO: nullable per retrocompat
}
// Quando DEVI rompere (rare):
// - Rimuovere un campo -> versione nuova
// - Cambiare tipo di un campo -> versione nuova
// - Cambiare semantica di un campo -> versione nuova
ETag i żądania warunkowe
L'ETag (Entity Tag) oraz mechanizm HTTP do zarządzania pamięcią podręczną i optymistyczna konkurencja. Każdy zasób ma skrót lub znacznik czasu identyfikujący jego wersję prąd:
// Server: risposta con ETag
GET /users/123
->
200 OK
ETag: "abc123def456"
Cache-Control: max-age=300
{
"id": 123,
"name": "Federico",
"version": 3
}
// Client: richiesta condizionale con If-None-Match
GET /users/123
If-None-Match: "abc123def456"
->
304 Not Modified // Risorsa non cambiata, usa la cache!
// Nessun body = traffico ridotto
// Se la risorsa e cambiata:
GET /users/123
If-None-Match: "abc123def456"
->
200 OK
ETag: "xyz789new123" // Nuovo ETag
{ /* dati aggiornati */ }
// ETag per concorrenza ottimistica (prevenire aggiornamenti in conflitto):
PUT /users/123
If-Match: "abc123def456" // "Aggiorna SOLO se la versione e ancora questa"
{...}
->
200 OK // Aggiornamento riuscito, nessun conflitto
// oppure
412 Precondition Failed // Qualcun altro ha modificato la risorsa!
// Il client deve rileggere prima di riaggiornare
Dokumentowanie za pomocą OpenAPI 3.1
OpenAPI 3.1 to branżowy standard dokumentowania interfejsów API REST. Ładny dokument OpenAPI pisemne służy jako formalna umowa, umożliwia generowanie pakietu SDK klienta i uprawnienia Interaktywna dokumentacja z interfejsem użytkownika Swagger lub Redoc:
// openapi.yaml - Struttura base di un documento OpenAPI 3.1
openapi: 3.1.0
info:
title: User Management API
version: 1.2.0
description: |
API per la gestione degli utenti dell'applicazione.
## Autenticazione
Usa Bearer token JWT nell'header Authorization.
contact:
name: API Support
email: api@example.com
license:
name: MIT
servers:
- url: https://api.example.com/v1
description: Production
- url: https://staging-api.example.com/v1
description: Staging
paths:
/users:
get:
operationId: listUsers
summary: Lista utenti
tags: [Users]
parameters:
- name: page
in: query
schema: { type: integer, minimum: 1, default: 1 }
- name: limit
in: query
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
- name: search
in: query
schema: { type: string }
responses:
'200':
description: Lista utenti paginata
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
'401':
$ref: '#/components/responses/Unauthorized'
post:
operationId: createUser
summary: Crea utente
tags: [Users]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: Utente creato
headers:
Location:
schema: { type: string }
description: URL del nuovo utente
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'409':
description: Email gia esistente
components:
schemas:
User:
type: object
required: [id, name, email, createdAt]
properties:
id: { type: integer, format: int64, readOnly: true }
name: { type: string, minLength: 1, maxLength: 100 }
email: { type: string, format: email }
createdAt: { type: string, format: date-time, readOnly: true }
CreateUserRequest:
type: object
required: [name, email, password]
properties:
name: { type: string, minLength: 1, maxLength: 100 }
email: { type: string, format: email }
password: { type: string, minLength: 8, writeOnly: true }
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- bearerAuth: []
Wzór odpowiedzi na błędy
Spójna struktura reakcji na błędy radykalnie usprawnia pracę programisty Doświadczenia konsumenckie API. Standard RFC 9457 (Szczegóły problemu) i stał się preferowanym wyborem w 2026 roku:
// RFC 9457 Problem Details for HTTP APIs
// Content-Type: application/problem+json
// 400 Bad Request
{
"type": "https://example.com/errors/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "The request body contains invalid data",
"instance": "/api/users",
"errors": [
{
"field": "email",
"message": "Invalid email format",
"value": "not-an-email"
},
{
"field": "password",
"message": "Password must be at least 8 characters",
"value": null
}
]
}
// 409 Conflict
{
"type": "https://example.com/errors/duplicate-email",
"title": "Duplicate Email",
"status": 409,
"detail": "An account with this email already exists",
"instance": "/api/users",
"email": "federico@example.com"
}
// 429 Too Many Requests
{
"type": "https://example.com/errors/rate-limited",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "Too many requests. Retry after 60 seconds.",
"retryAfter": 60
}
Wnioski i dalsze kroki
Profesjonalne API REST w 2026 roku działa na poziomie 2 Modelu Dojrzałości Richardsona (zasoby + poprawne czasowniki HTTP + odpowiednie kody statusu), użyj ETag do buforowania i optymistycznej współbieżności, przyjmuje zrównoważoną wersję w oparciu o zmiany addytywne i dokumentuje formalną umowę z OtwarteAPI 3.1. W większości kontekstów osiągnięcie poziomu 3 HATEOAS nie jest konieczne.
Następny artykuł analizuje GraphQL dogłębnie: jak działa system resolwerów, ponieważ problem N+1 jest najczęstszym ryzykiem architektonicznym, oraz jako DataLoader rozwiązuje grupowanie zapytań do bazy danych.
Seria: Projektowanie API — porównanie REST, GraphQL, gRPC i tRPC
- Artykuł 1: Krajobraz API w 2026 r. – matryca decyzyjna
- Artykuł 2 (ten): REST w 2026 r. — Najlepsze praktyki, wersjonowanie i model dojrzałości Richardsona
- Artykuł 3: GraphQL — język zapytań, narzędzie do rozwiązywania problemów i problem N+1
- Artykuł 4: Federacja GraphQL — Supergraph, Subgraph i Router Apollo
- Artykuł 5: gRPC — Protobuf, wydajność i komunikacja między usługami
- Artykuł 6: tRPC — kompleksowe bezpieczeństwo typu bez generowania kodu
- Artykuł 7: Elementy webhook — wzorce, zabezpieczenia i logika ponownych prób
- Artykuł 8: Wersjonowanie API – URI, nagłówki i zasady wycofywania
- Artykuł 9: Ograniczanie i dławienie szybkości — algorytmy i implementacje
- Artykuł 10: Architektura hybrydowego API – REST + tRPC + gRPC w 2026 r







