Skip to content

Detection Strategies

Jean-Marc Strauven edited this page Dec 23, 2025 · 3 revisions

Detection Strategies

Learn about the different ways to detect API versions from incoming requests.


Overview

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

URI Path Strategy (Default)

Version is embedded in the URL path.

Configuration

// config/apiroute.php
'strategy' => 'uri',

'strategies' => [
    'uri' => [
        'prefix' => 'api',           // /api/v1/users
        'pattern' => 'v{version}',   // v1, v2, etc.
    ],
],

Usage

GET /api/v1/users HTTP/1.1
Host: example.com
GET /api/v2/users HTTP/1.1
Host: example.com

Pros & Cons

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

Custom Patterns

'strategies' => [
    'uri' => [
        'prefix' => 'service',       // /service/v1/users
        'pattern' => 'v{version}',
    ],
],

Header Strategy

Version is specified in a custom HTTP header.

Configuration

// config/apiroute.php
'strategy' => 'header',

'strategies' => [
    'header' => [
        'name' => 'X-API-Version',   // Header name
    ],
],

Usage

GET /api/users HTTP/1.1
Host: example.com
X-API-Version: 2

Or with v prefix:

GET /api/users HTTP/1.1
Host: example.com
X-API-Version: v2

Pros & Cons

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

Alternative Header Names

'strategies' => [
    'header' => [
        'name' => 'Api-Version',      // Shorter name
        // or
        'name' => 'X-My-App-Version', // Custom prefix
    ],
],

Query Parameter Strategy

Version is specified as a query parameter.

Configuration

// config/apiroute.php
'strategy' => 'query',

'strategies' => [
    'query' => [
        'parameter' => 'api_version', // Query parameter name
    ],
],

Usage

GET /api/users?api_version=2 HTTP/1.1
Host: example.com

Pros & Cons

Pros Cons
Easy to test Pollutes URL
Works everywhere May affect caching
Simple implementation Less "clean"

Alternative Parameter Names

'strategies' => [
    'query' => [
        'parameter' => 'v',          // Short: ?v=2
        // or
        'parameter' => 'version',    // Explicit: ?version=2
    ],
],

Accept Header Strategy

Version is specified in the Accept header using content negotiation.

Configuration

// config/apiroute.php
'strategy' => 'accept',

'strategies' => [
    'accept' => [
        'pattern' => 'application/vnd.{vendor}.{version}+json',
        'vendor' => env('API_VENDOR', 'api'),
    ],
],

Usage

GET /api/users HTTP/1.1
Host: example.com
Accept: application/vnd.api.v2+json

With custom vendor:

GET /api/users HTTP/1.1
Host: example.com
Accept: application/vnd.mycompany.v2+json

Pros & Cons

Pros Cons
Standard HTTP approach Complex syntax
Content negotiation support Harder to test
Clean URLs Less intuitive
Used by GitHub API

Custom Patterns

'strategies' => [
    'accept' => [
        'pattern' => 'application/vnd.{vendor}.{version}+json',
        'vendor' => 'mycompany',  // application/vnd.mycompany.v2+json
    ],
],

Choosing a Strategy

Decision Guide

┌─────────────────────────────────────────────────────────────┐
│                    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               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Recommendations by Use Case

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

Detection Strategy Enum

use Grazulex\ApiRoute\Support\DetectionStrategy;

enum DetectionStrategy: string
{
    case Uri = 'uri';
    case Header = 'header';
    case Query = 'query';
    case Accept = 'accept';
}

Runtime Strategy Detection

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);

Default Version Behavior

When no version is specified:

// config/apiroute.php
'default_version' => 'latest',  // Use most recent non-beta version
// or
'default_version' => 'v2',      // Always default to v2

Combining with Fallback

If 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

Next Steps

Clone this wiki locally