API versioning is crucial for maintaining backward compatibility while evolving your application. In this guide, we’ll implement a clean, scalable API versioning system in Laravel 12 using route prefixes, middleware, and versioned controllers.
🚀 Step 1: Setting Up API Versioning in Laravel
1. Configure the Base API Routes (routes/api.php)
Instead of dumping all routes in one file, we split them into versioned route files.
<?php
// routes/api.php
use Illuminate\Support\Facades\Route;
Route::prefix('v1')->group(base_path('routes/api/v1.php'));
Route::prefix('v2')->group(base_path('routes/api/v2.php'));
2. Create Version-Specific Route Files
Store versioned routes in routes/api/v1.php and routes/api/v2.php.
Example (routes/api/v1.php):
<?php
use App\Http\Controllers\API\V1\UserController;
use Illuminate\Support\Facades\Route;
Route::apiResource('users', UserController::class);
🛠️ Step 2: Organizing Versioned Controllers
Keep controllers separated by version:
app/Http/Controllers/API/
├── V1/
│ ├── UserController.php
├── V2/
│ ├── UserController.php
🔧 Step 3: Adding API Version Middleware
To track the API version in use, create a middleware:
php artisan make:middleware ApiVersionMiddleware
Update the middleware (app/Http/Middleware/ApiVersionMiddleware.php):
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ApiVersionMiddleware
{
/**
* Handle an incoming request.
*
* @param Request
* @param Closure $next
* @param string $version
*/
public function handle(Request $request, Closure $next, string $version)
{
// Set the API version in config
config(['app.api_version' => $version]);
return $next($request)->header('X-API-Version', $version);
}
}
Register it in bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
apiPrefix: 'api',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'api.version' => \App\Http\Middleware\ApiVersionMiddleware::class,
]);
})
->create();
Apply middleware in routes (routes/api.php):
<?php
// routes/api.php
use Illuminate\Support\Facades\Route;
Route::prefix('v1')
->middleware('api.version:v1')
->group(base_path('routes/api/v1.php'));
Route::prefix('v2')
->middleware('api.version:v2')
->group(base_path('routes/api/v2.php'));
✅ Step 4: Testing Versioned APIs
<?php
namespace Tests\Feature\feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class AuthTest extends TestCase
{
/**
* A basic feature test example.
*/
public function test_v1_login()
{
$response = $this->postJson('/api/v1/login', [
'email' => 'test@example.com',
'password' => 'test@example.com'
]);
$response->assertStatus(200)
->assertHeader('X-API-Version', 'v1');
}
}
To run a single test file:
//test file : AuthTest
//testsuite can be Feature or Unit
php artisan test --testsuite=Feature --filter=AuthTest
To run a specific test method:
php artisan test --testsuite=Feature --filter=test_v1_login
Test Result:
PASS Tests\Feature\feature\AuthTest
✓ v1 login 2.70s
Tests: 1 passed (3 assertions)
Duration: 2.92s
🚀 Final Thoughts & Best Practices
✔ Use Semantic Versioning (v1, v2) – Helps track breaking changes.
✔ Keep Controllers Thin – Delegate logic to services.
✔ Document API Changes – Use Swagger/OpenAPI for each version.
📌 Conclusion
By following this structured approach, you can maintain multiple API versions cleanly in Laravel 12. This ensures backward compatibility while allowing smooth transitions to newer versions.
💬 What’s your API versioning strategy? Let’s discuss in the comments!
If you found this helpful, feel free to share or drop a comment. Happy coding with Laravel! 🧱✨
Free resources for this topic
Code snippets and fixes for this topic
Get one practical tip every week
New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.
Free download: Laravel interview questions (PDF)
Have a question about this post?
Ask the community: other developers and the author can answer.
Get notified about new articles
One click, no email needed. Turn it off anytime with the bell at the top.
Get new articles on your iPhone
Add DEV Talk to your Home Screen, open it, then tap the bell.
Enjoying the content?
If this article helped you, you can support the author with a coffee. It keeps new posts coming.
Comments 0
No comments yet
Be the first to share your thoughts.