Prečo väčšina API „vyhorí"
API „vyhorí" keď pridáte druhého klienta a zistíte, že odpovede sú nekonzistentné. Alebo keď po roku potrebujete pridať pole do odpovede a neviete, ktorí klienti to prežijú. Alebo keď bot zalapá endpoint a aplikácia padne. Všetkým týmto problémom sa dá predísť rozhodnutiami, ktoré urobíte v prvý deň.
Verziovanie od prvého dňa
Verziovanie API je jediný spôsob, ako môžete neskôr urobiť breaking change bez toho, aby ste rozbili existujúcich klientov. V Laraveli mám dve stratégie:
URL verziovanie (odporúčané pre verejné API)
// routes/api.php
Route::prefix('v1')->group(base_path('routes/api_v1.php'));
Route::prefix('v2')->group(base_path('routes/api_v2.php'));
// Výsledok:
// GET /api/v1/users
// GET /api/v2/users
Header verziovanie (pre interné API)
// Klient posiela:
// Accept: application/vnd.myapp.v2+json
// Middleware číta verziu z Accept hlavičky
class ApiVersionMiddleware {
public function handle($request, Closure $next) {
$version = $this->parseVersion($request->header('Accept'));
app()->instance('api.version', $version);
return $next($request);
}
}
?version=2). Nie je to cacheable a klienti to ľahko zabudnú posielať.
Autentifikácia: Sanctum vs. Passport
Toto je najčastejšia otázka pri Laravel API. Odpoveď závisí od use case:
| Kritérium | Laravel Sanctum | Laravel Passport |
|---|---|---|
| SPA autentifikácia | ✅ Ideálne (cookie-based) | Zbytočne komplexné |
| Mobilné aplikácie | ✅ API tokeny | ✅ OAuth2 tokeny |
| Third-party OAuth2 | ❌ Nepodporuje | ✅ Plná podpora |
| Jednoduchosť setup-u | ✅ 15 minút | 2–4 hodiny |
| Token scopes | Jednoduché | ✅ Granulárne |
Pravidlo: Pre 90 % projektov (SPA + mobilná aplikácia) použite Sanctum. Passport potrebujete len ak implementujete OAuth2 server — teda vydávate tokeny pre tretie strany.
// Inštalácia Sanctum
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
// Vydanie tokenu
$token = $user->createToken('mobile-app', ['read:orders', 'write:orders'])->plainTextToken;
// Ochrana routy
Route::middleware('auth:sanctum')->get('/user', fn(Request $r) => $r->user());
Konzistentná štruktúra odpovedí
Každý endpoint by mal vracať rovnako štruktúrovanú odpoveď. Klienti (mobilné aplikácie, frontend) potom môžu napísať jeden parser a použiť ho všade. Používam tento vzor:
// Úspech
{
"data": { "id": 1, "name": "Ján Novák" },
"meta": { "version": "v1", "timestamp": "2026-05-26T10:00:00Z" }
}
// Zoznam
{
"data": [{ "id": 1 }, { "id": 2 }],
"meta": { "total": 100, "per_page": 15, "current_page": 1 }
}
// Chyba (RFC 7807 Problem Details)
{
"type": "https://www.gear.sk/errors/validation",
"title": "Chyba validácie",
"status": 422,
"detail": "Pole email je povinné.",
"errors": { "email": ["Pole email je povinné."] }
}
API Resource v Laraveli
// app/Http/Resources/UserResource.php
class UserResource extends JsonResource {
public function toArray(Request $request): array {
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// Citlivé polia nikdy nevracajte (password_hash, remember_token)
];
}
}
// V controlleri
return UserResource::collection(User::paginate(15));
Rate limiting — ochrana pred botmi a zneužitím
Laravel má vstavaný rate limiter, stačí ho nakonfigurovať v RouteServiceProvider:
// app/Providers/RouteServiceProvider.php
RateLimiter::for('api', function (Request $request) {
return $request->user()
? Limit::perMinute(120)->by($request->user()->id) // autentifikovaný
: Limit::perMinute(20)->by($request->ip()); // anonymný
});
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->ip()); // brute force ochrana
});
// routes/api.php
Route::middleware(['throttle:api'])->group(function () {
Route::post('/login', LoginController::class)->middleware('throttle:login');
Route::middleware('auth:sanctum')->group(function () {
// chránené endpointy
});
});
Pri překročení limitu Laravel automaticky vráti HTTP 429 s hlavičkou Retry-After.
Error handling podľa RFC 7807
RFC 7807 (Problem Details for HTTP APIs) je štandard pre štruktúrované chybové odpovede. Laravel 11+ ho podporuje natívne cez respondWithProblem(), v starších verziách si vytvoríte vlastnú triedu:
// app/Exceptions/Handler.php
public function register(): void {
$this->renderable(function (ValidationException $e, Request $request) {
if ($request->expectsJson()) {
return response()->json([
'type' => 'https://www.gear.sk/errors/validation',
'title' => 'Chyba validácie',
'status' => 422,
'detail' => 'Niektoré polia obsahujú neplatné hodnoty.',
'errors' => $e->errors(),
], 422);
}
});
$this->renderable(function (ModelNotFoundException $e, Request $request) {
if ($request->expectsJson()) {
return response()->json([
'type' => 'https://www.gear.sk/errors/not-found',
'title' => 'Záznam nenájdený',
'status' => 404,
'detail' => 'Požadovaný zdroj neexistuje.',
], 404);
}
});
}
Dokumentácia cez Scramble
Scramble je najlepší Laravel package pre automatickú OpenAPI dokumentáciu — číta vaše PHP kód (typy, PHPDoc, Resource triedy) a generuje interaktívnu dokumentáciu bez toho, aby ste pisali YAML ručne.
composer require dedoc/scramble
# Dokumentácia je dostupná na:
# http://localhost/docs/api
: UserResource, : UserCollection) a automaticky generuje správnu schému. Napíšte správne typy a dokumentácia sa postará sama.
Testovanie — každý endpoint má test
API bez testov je API, ktoré sa bojíte zmeniť. V Laraveli s Pest-om je to rýchle:
// tests/Feature/Api/V1/UserTest.php
it('returns authenticated user', function () {
$user = User::factory()->create();
$response = $this->actingAs($user, 'sanctum')
->getJson('/api/v1/user');
$response
->assertOk()
->assertJsonStructure(['data' => ['id', 'name', 'email']]);
});
it('rejects unauthenticated requests', function () {
$this->getJson('/api/v1/user')->assertUnauthorized();
});
it('throttles login after 5 attempts', function () {
foreach (range(1, 5) as $_) {
$this->postJson('/api/v1/login', ['email' => 'x@x.sk', 'password' => 'wrong']);
}
$this->postJson('/api/v1/login', ['email' => 'x@x.sk', 'password' => 'wrong'])
->assertStatus(429);
});
Cache a ETag pre výkonné API
Pre endpointy, ktoré vracajú dáta, ktoré sa nemenia každú sekundu, pridajte caching. Laravel má vstavaný Cache facade a podpora ETag je jednoduchá:
// Controller s cache
public function index(Request $request): JsonResponse {
$data = Cache::remember('products:all', 300, fn() =>
ProductResource::collection(Product::active()->get())
);
$etag = md5(json_encode($data));
if ($request->header('If-None-Match') === $etag) {
return response()->json(null, 304);
}
return response()->json(['data' => $data])
->header('ETag', $etag)
->header('Cache-Control', 'public, max-age=300');
}
Záver: checklist pred spustením
- URL verziovanie implementované (
/api/v1/) - Autentifikácia cez Sanctum (alebo Passport pre OAuth2)
- Konzistentná štruktúra všetkých odpovedí
- Rate limiting na anonymných aj autentifikovaných endpointoch
- RFC 7807 error responses
- OpenAPI dokumentácia cez Scramble
- Feature testy pre každý endpoint
- ETag/Cache-Control na čítacích endpointoch
- HTTPS a bezpečnostné hlavičky na serveri
REST API navrhnuté s týmito princípmi vydržia roky bez nutnosti rewrite-u. Ak riešite návrh alebo migráciu existujúceho API, rád pomôžem s auditom a odhadom nákladov.