-
-
Notifications
You must be signed in to change notification settings - Fork 4
Migration v1 to v2
This guide helps you migrate from laravel-apiroute v1.x to v2.0.
v2.0 introduces configuration-based version registration as the recommended approach. This change ensures versions are properly registered on every application boot, solving the issue where versions were lost between tests.
// routes/api.php or RouteServiceProvider::boot()
ApiRoute::version('v1', function () {
Route::get('users', [UserController::class, 'index']);
})->current();// config/apiroute.php
'versions' => [
'v1' => [
'routes' => base_path('routes/api/v1.php'),
'middleware' => ['auth:sanctum'],
'status' => 'active',
],
],// routes/api/v1.php
Route::get('users', [UserController::class, 'index']);In v1.x, when running tests, Laravel creates a new application container between tests. The ApiRouteManager singleton was recreated with an empty versions collection, and the RouteServiceProvider::boot() method (where ApiRoute::version() was called) was not re-executed.
The configuration-based approach ensures versions are loaded from config on every boot(), making it work reliably in all scenarios including:
- Feature tests with
RefreshDatabase - Parallel testing
- Queue workers
- Octane applications
Create a dedicated file for each API version:
routes/
├── api/
│ ├── v1.php
│ ├── v2.php
│ └── v3.php
Move your route definitions from the closure to these files:
// routes/api/v1.php
<?php
use App\Http\Controllers\Api\V1\UserController;
use Illuminate\Support\Facades\Route;
Route::apiResource('users', UserController::class);Add the versions section to your config/apiroute.php:
'versions' => [
'v1' => [
'routes' => base_path('routes/api/v1.php'),
'middleware' => ['auth:sanctum'],
'status' => 'deprecated',
'deprecated_at' => '2025-06-01',
'sunset_at' => '2025-12-01',
'successor' => 'v2',
'documentation' => 'https://docs.myapp.com/api/v1',
'rate_limit' => 100,
],
'v2' => [
'routes' => base_path('routes/api/v2.php'),
'middleware' => ['auth:sanctum'],
'status' => 'active',
'documentation' => 'https://docs.myapp.com/api/v2',
'rate_limit' => 1000,
],
'v3' => [
'routes' => base_path('routes/api/v3.php'),
'middleware' => ['auth:sanctum'],
'status' => 'beta',
],
],Remove the ApiRoute::version() calls from your routes/api.php or RouteServiceProvider:
// Before: routes/api.php
ApiRoute::version('v1', function () {
Route::apiResource('users', UserController::class);
})->current();
// After: routes/api.php
// Empty or with non-versioned routesIf you've previously published the config, you may need to update it:
php artisan vendor:publish --tag="apiroute-config" --forceThen merge your existing settings with the new versions section.
| Option | Type | Description |
|---|---|---|
routes |
string |
Path to route file (required) |
middleware |
array |
Additional middleware |
status |
string |
active, beta, deprecated, sunset
|
deprecated_at |
string|null |
Deprecation date (Y-m-d) |
sunset_at |
string|null |
Sunset date (Y-m-d) |
successor |
string|null |
Successor version name |
documentation |
string|null |
Documentation URL |
rate_limit |
int|null |
Requests per minute |
The ApiRoute::version() method still works but is not recommended for new projects. If you're using it, be aware that:
- Versions registered via
ApiRoute::version()may not persist between tests - You may need to call
ApiRoute::version()in your test setup
If you must use the legacy approach, reset and reload versions in your test setup:
beforeEach(function () {
// Reset the manager
app(ApiRouteManager::class)->reset();
// Re-register versions
ApiRoute::version('v1', function () {
Route::get('test', fn () => response()->json(['ok' => true]));
})->current();
// Boot to apply config versions (if any)
app(ApiRouteManager::class)->boot();
});Yes, but it's not recommended. Configuration-based versions are loaded first, then ApiRoute::version() calls add additional versions. However, the imperative versions will still have the persistence issue in tests.
No, controllers remain unchanged. Only how you register routes changes.
Route naming works the same way. Define names in your route files:
// routes/api/v1.php
Route::get('users', [UserController::class, 'index'])->name('api.v1.users.index');- Version Declaration - Learn about config-based declaration
- Configuration - Full configuration reference
Laravel ApiRoute - Complete API versioning lifecycle management for Laravel
Home | Getting Started | Examples | Configuration
Made with ❤️ for the Laravel community