-
-
Notifications
You must be signed in to change notification settings - Fork 4
Detection Strategies
Jean-Marc Strauven edited this page Dec 23, 2025
·
3 revisions
Learn about the different ways to detect API versions from incoming requests.
Laravel ApiRoute supports four detection strategies:
| Strategy | Example | Best For |
|---|---|---|
| URI (default) | /api/v1/users |
Public APIs, RESTful design |
| Header | X-API-Version: 1 |
Mobile apps, SDKs |
| Query | ?api_version=1 |
Simple integration, debugging |
| Accept | Accept: application/vnd.api.v1+json |
Content negotiation |
Version is embedded in the URL path.
// config/apiroute.php
'strategy' => 'uri',
'strategies' => [
'uri' => [
'prefix' => 'api', // /api/v1/users
'pattern' => 'v{version}', // v1, v2, etc.
],
],GET /api/v1/users HTTP/1.1
Host: example.comGET /api/v2/users HTTP/1.1
Host: example.com| Pros | Cons |
|---|---|
| Clear and visible in URL | URLs change between versions |
| Easy to document | Harder to switch versions |
| Cacheable | Requires URL structure planning |
| Browser-friendly |
'strategies' => [
'uri' => [
'prefix' => 'service', // /service/v1/users
'pattern' => 'v{version}',
],
],Version is specified in a custom HTTP header.
// config/apiroute.php
'strategy' => 'header',
'strategies' => [
'header' => [
'name' => 'X-API-Version', // Header name
],
],GET /api/users HTTP/1.1
Host: example.com
X-API-Version: 2Or with v prefix:
GET /api/users HTTP/1.1
Host: example.com
X-API-Version: v2| Pros | Cons |
|---|---|
| Clean URLs | Harder to test in browser |
| Easy version switching | Header might be stripped by proxies |
| Same endpoint for all versions | Not visible in URL |
'strategies' => [
'header' => [
'name' => 'Api-Version', // Shorter name
// or
'name' => 'X-My-App-Version', // Custom prefix
],
],Version is specified as a query parameter.
// config/apiroute.php
'strategy' => 'query',
'strategies' => [
'query' => [
'parameter' => 'api_version', // Query parameter name
],
],GET /api/users?api_version=2 HTTP/1.1
Host: example.com| Pros | Cons |
|---|---|
| Easy to test | Pollutes URL |
| Works everywhere | May affect caching |
| Simple implementation | Less "clean" |
'strategies' => [
'query' => [
'parameter' => 'v', // Short: ?v=2
// or
'parameter' => 'version', // Explicit: ?version=2
],
],Version is specified in the Accept header using content negotiation.
// config/apiroute.php
'strategy' => 'accept',
'strategies' => [
'accept' => [
'pattern' => 'application/vnd.{vendor}.{version}+json',
'vendor' => env('API_VENDOR', 'api'),
],
],GET /api/users HTTP/1.1
Host: example.com
Accept: application/vnd.api.v2+jsonWith custom vendor:
GET /api/users HTTP/1.1
Host: example.com
Accept: application/vnd.mycompany.v2+json| Pros | Cons |
|---|---|
| Standard HTTP approach | Complex syntax |
| Content negotiation support | Harder to test |
| Clean URLs | Less intuitive |
| Used by GitHub API |
'strategies' => [
'accept' => [
'pattern' => 'application/vnd.{vendor}.{version}+json',
'vendor' => 'mycompany', // application/vnd.mycompany.v2+json
],
],┌─────────────────────────────────────────────────────────────┐
│ Choose Your Strategy │
├─────────────────────────────────────────────────────────────┤
│ │
│ Is URL visibility important? │
│ ├─ Yes → URI Strategy │
│ └─ No │
│ │ │
│ ├─ Need content negotiation? → Accept Strategy │
│ ├─ Simple integration needed? → Query Strategy │
│ └─ Mobile/SDK focused? → Header Strategy │
│ │
└─────────────────────────────────────────────────────────────┘
| Use Case | Recommended Strategy |
|---|---|
| Public REST API | URI |
| Mobile Applications | Header |
| Internal Services | Header or Query |
| Third-party Integrations | URI |
| Developer Debugging | Query |
| Content Negotiation | Accept |
use Grazulex\ApiRoute\Support\DetectionStrategy;
enum DetectionStrategy: string
{
case Uri = 'uri';
case Header = 'header';
case Query = 'query';
case Accept = 'accept';
}The VersionResolver handles version detection:
use Grazulex\ApiRoute\VersionResolver;
$resolver = app(VersionResolver::class);
// Get the resolved version definition
$version = $resolver->resolve($request);
// Get the raw requested version string
$requestedVersion = $resolver->getRequestedVersion($request);When no version is specified:
// config/apiroute.php
'default_version' => 'latest', // Use most recent non-beta version
// or
'default_version' => 'v2', // Always default to v2If a route doesn't exist in the requested version:
// config/apiroute.php
'fallback' => [
'enabled' => true,
'strategy' => 'previous', // Try previous version
'add_header' => true, // Add X-API-Version-Fallback header
],Response when fallback is used:
HTTP/1.1 200 OK
X-API-Version: v2
X-API-Version-Fallback: v1- HTTP Headers - Headers added to responses
- Configuration - Full configuration reference
- Middleware - How version resolution works
Laravel ApiRoute - Complete API versioning lifecycle management for Laravel
Home | Getting Started | Examples | Configuration
Made with ❤️ for the Laravel community