$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[September 29, 2025] Laravel

Laravel API Versioning: Strategies

Laravel API Versioning: Strategies

────────────────────────────────────────────────────────

Laravel API Versioning: Strategies

Tip: URL-Based Versioning

Route::prefix('v1')->group(function () {
    Route::get('/users', [V1\UserController::class, 'index']);
});

Simplest approach — version in the URL.

Gotcha: Header-Based Versioning

Route::middleware('api.version')->group(function () {
    // Version determined by middleware
});

Cleaner URLs but harder to test and cache.

Tip: API Resources per Version

Each version has its own resource transformer class.

Gotcha: Breaking Changes

Never change existing API behavior. Add new fields or endpoints instead.

Tip: Sunset Header

return response()->json($data)
    ->header('Sunset', '2025-12-31')
    ->header('Deprecation', 'true');

Gotcha: Version Fallback

Consider supporting a default version when none is specified, but log a deprecation warning.

Tip: Use route:cache Carefully

php artisan route:cache is fast, but it doesn't work with closure-based routes. Every time you cache routes, Laravel serializes them. If you have Route::redirect() or closure callbacks, the cache breaks. Stick to controller-based routes in production.

Tip: Model APP_KEY Rotation

Rotating APP_KEY invalidates all encrypted data — cookies, encrypted DB columns, and password reset tokens. If you must rotate (e.g., after a leak), plan a migration that re-encrypts existing data with the new key.

Gotcha: Local Scope Leaks

Global scopes defined in booted() apply to ALL queries on that model — including relationships. An innocent User::all() in admin panel might exclude soft-deleted users if a global scope is active.

Senior Insight

API design is where framework knowledge meets engineering discipline. The mistake I see most often is versioning through URLs (/api/v1/) instead of through content negotiation or headers. URL versioning locks you into maintaining parallel route files forever. I've also learned the hard way that API Resources should use ::$wrap = false to avoid the data envelope when your frontend team doesn't expect it — align on contract format before writing a single endpoint.

Source: Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/laravel), Spatie Blog (https://spatie.be/blog)

────────────────────────────────────────────────────────
<-- back to posts