-
-
Notifications
You must be signed in to change notification settings - Fork 4
Middleware
Learn about the middleware that powers Laravel ApiRoute.
Laravel ApiRoute provides three middleware classes:
| Middleware | Purpose |
|---|---|
ResolveApiVersion |
Main middleware - resolves and validates versions |
EnforceApiVersion |
Force a specific version |
TrackApiUsage |
Track API usage statistics |
RateLimitApiVersion |
Version-specific rate limiting |
The main middleware that handles version resolution.
- Resolves the requested API version
- Validates the version exists
- Checks sunset status
- Stores version in request
- Dispatches events for deprecated versions
- Adds response headers
Request
│
▼
┌─────────────────────────────┐
│ 1. Resolve version │
│ (URI/Header/Query) │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 2. Version exists? │──No──→ VersionNotFoundException
└─────────────────────────────┘
│ Yes
▼
┌─────────────────────────────┐
│ 3. Is sunset? │──Yes─→ VersionSunsetException
└─────────────────────────────┘ (if action = 'reject')
│ No
▼
┌─────────────────────────────┐
│ 4. Store in request │
│ - api_version │
│ - api_version_definition│
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 5. Is deprecated? │──Yes─→ Dispatch DeprecatedVersionAccessed
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 6. Execute controller │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ 7. Add response headers │
└─────────────────────────────┘
│
▼
Response
class ResolveApiVersion
{
public function __construct(
private readonly VersionResolverInterface $resolver,
private readonly VersionHeaders $headers
) {}
public function handle(Request $request, Closure $next): Response
{
// 1. Resolve the requested version
$version = $this->resolver->resolve($request);
// 2. Verify that the version exists
if ($version === null) {
throw new VersionNotFoundException(
$this->resolver->getRequestedVersion($request)
);
}
// 3. Check if sunset
if ($version->isSunset() && config('apiroute.sunset.action') === 'reject') {
throw new VersionSunsetException($version);
}
// 4. Store the version in the request
$request->attributes->set('api_version', $version->name());
$request->attributes->set('api_version_definition', $version);
// 5. Dispatch event if deprecated version
if ($version->isDeprecated()) {
event(new DeprecatedVersionAccessed($version, $request));
}
// 6. Execute the request
$response = $next($request);
// 7. Add version headers
return $this->headers->addToResponse($response, $version);
}
}Force a specific API version regardless of request.
// In routes/api.php
Route::middleware('api.version:v2')->group(function () {
Route::get('users', [UserController::class, 'index']);
});- Force all routes to use a specific version
- Override version detection for specific endpoints
- Testing and debugging
Optional middleware for tracking API usage statistics.
// config/apiroute.php
'tracking' => [
'enabled' => true,
'driver' => 'database',
'table' => 'api_version_stats',
'aggregate' => 'hourly',
],- Version used
- Endpoint accessed
- HTTP method
- Response status (success/error)
- Timestamp
Tracking is done asynchronously to avoid impacting response time:
dispatch(function () use ($request, $response) {
$this->tracker->track(
version: $request->attributes->get('api_version'),
endpoint: $request->path(),
method: $request->method(),
status: $response->status()
);
})->afterResponse();Apply version-specific rate limiting.
ApiRoute::version('v1', function () {
// ...
})->rateLimit(100); // 100 requests per minute
ApiRoute::version('v2', function () {
// ...
})->rateLimit(1000); // 1000 requests per minuteEncourage migration from deprecated versions by applying stricter rate limits.
// config/apiroute.php
'middleware' => [
'group' => 'api', // Apply to this middleware group
'alias' => 'api.version', // Middleware alias
],If auto-discovery is disabled, register manually:
// In bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'api.version' => \Grazulex\ApiRoute\Middleware\ResolveApiVersion::class,
]);
})Or in app/Http/Kernel.php (Laravel 10):
protected $middlewareAliases = [
'api.version' => \Grazulex\ApiRoute\Middleware\ResolveApiVersion::class,
];After the middleware runs, version information is available:
public function index(Request $request)
{
$version = $request->attributes->get('api_version'); // 'v1'
$definition = $request->attributes->get('api_version_definition'); // VersionDefinition
}public function index(Request $request)
{
$version = $request->apiVersion(); // 'v1'
$status = $request->apiVersionStatus(); // VersionStatus::Active
$definition = $request->apiVersionDefinition(); // VersionDefinition
if ($request->isDeprecatedVersion()) {
// Handle deprecated version
}
}public function index()
{
$version = api_version(); // 'v1'
$definition = api_version_definition(); // VersionDefinition
}Create custom middleware that extends the base:
<?php
namespace App\Http\Middleware;
use Closure;
use Grazulex\ApiRoute\Middleware\ResolveApiVersion;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class CustomApiVersionMiddleware extends ResolveApiVersion
{
public function handle(Request $request, Closure $next): Response
{
// Pre-processing
Log::info('API request', [
'path' => $request->path(),
'ip' => $request->ip(),
]);
// Call parent middleware
$response = parent::handle($request, $next);
// Post-processing
$response->headers->set('X-Custom-Header', 'value');
return $response;
}
}The recommended middleware order:
Route::middleware([
'api',
'api.version', // Resolve version first
'throttle:api', // Then rate limit
'auth:sanctum', // Then authenticate
])->group(function () {
// Routes...
});- Events - Events dispatched by middleware
- Exceptions - Exception handling
- Configuration - Full configuration reference
Laravel ApiRoute - Complete API versioning lifecycle management for Laravel
Home | Getting Started | Examples | Configuration
Made with ❤️ for the Laravel community