shahghasiadil/laravel-api-versioning

Elegant attribute-based API versioning solution for Laravel applications with built-in deprecation management and version inheritance

Maintainers

Package info

github.com/shahghasiadil/laravel-api-versioning

pkg:composer/shahghasiadil/laravel-api-versioning

Fund package maintenance!

:vendor_name

Statistics

Installs: 13 638

Dependents: 0

Suggesters: 0

Stars: 29

Open Issues: 2


README

Latest Version on Packagist Total Downloads License

Attribute-based API versioning for Laravel, with version detection, deprecation metadata, versioned resources, and route inspection commands.

Requirements

  • PHP 8.2+
  • Laravel 10, 11, or 12

Installation

You can install the package via composer:

composer require shahghasiadil/laravel-api-versioning

Publish the config file:

php artisan vendor:publish --provider="ShahGhasiAdil\LaravelApiVersioning\ApiVersioningServiceProvider" --tag="config"

Quick Start

1. Configure supported versions

Edit config/api-versioning.php:

return [
    'default_version' => '2.0',
    'supported_versions' => ['1.0', '1.1', '2.0', '2.1'],

    'detection_methods' => [
        'header' => ['enabled' => true, 'header_name' => 'X-API-Version'],
        'query' => ['enabled' => true, 'parameter_name' => 'api-version'],
        'path' => ['enabled' => true, 'prefix' => 'api/v'],
        'media_type' => ['enabled' => false, 'format' => 'application/vnd.api+json;version=%s'],
    ],
];

2. Apply middleware to API routes

api.version middleware alias is registered by the package.

use Illuminate\Support\Facades\Route;

Route::middleware('api.version')->group(function () {
    Route::apiResource('users', UserController::class);
});

3. Add version attributes to your controller

use ShahGhasiAdil\LaravelApiVersioning\Attributes\ApiVersion;
use ShahGhasiAdil\LaravelApiVersioning\Attributes\Deprecated;
use ShahGhasiAdil\LaravelApiVersioning\Attributes\MapToApiVersion;
use ShahGhasiAdil\LaravelApiVersioning\Traits\HasApiVersionAttributes;

#[ApiVersion(['2.0', '2.1'])]
class UserController extends Controller
{
    use HasApiVersionAttributes;

    public function index(): JsonResponse
    {
        return response()->json([
            'version' => $this->getCurrentApiVersion(),
            'deprecated' => $this->isVersionDeprecated(),
        ]);
    }

    #[MapToApiVersion(['2.1'])]
    public function store(Request $request): JsonResponse
    {
        return response()->json(['message' => 'Created']);
    }

    #[MapToApiVersion(['2.0'])]
    #[Deprecated(message: 'Use store() instead', replacedBy: '2.1', sunsetDate: '2026-12-31')]
    public function create(Request $request): JsonResponse
    {
        return response()->json(['message' => 'Deprecated endpoint']);
    }
}

4. Call your API with a version

# Header
curl -H "X-API-Version: 2.0" https://api.example.com/api/users

# Query string
curl "https://api.example.com/api/users?api-version=2.0"

# Path-based
curl https://api.example.com/api/v2.0/users

# Media type
curl -H "Accept: application/vnd.api+json;version=2.0" https://api.example.com/api/users

Features

  • Controller and method level attributes (ApiVersion, MapToApiVersion)
  • Version-neutral endpoints (ApiVersionNeutral)
  • Deprecation metadata (Deprecated with message, sunset date, replacement)
  • Multiple version detection methods (header, query, path, media type)
  • Response headers for active/supported/deprecated versions
  • Version-aware resources and resource collections
  • Version inheritance and method mapping for resources
  • RFC 7807 problem+json error responses for unsupported versions
  • Built-in version comparison utilities
  • Cache layer for attribute resolution
  • Artisan commands for generation, inspection, config and health checks
  • Testing base class with version request/assert helpers

Attributes

ApiVersion

Use on a controller or method.

#[ApiVersion('2.0')]
#[ApiVersion(['1.0', '1.1', '2.0'])]

MapToApiVersion

Use on a method to map it to specific versions.

#[MapToApiVersion(['2.0', '2.1'])]

ApiVersionNeutral

Marks controller/method as version-neutral (available in all supported versions).

use ShahGhasiAdil\LaravelApiVersioning\Attributes\ApiVersionNeutral;

#[ApiVersionNeutral]
class HealthController extends Controller
{
    // ...
}

Deprecated

Add deprecation metadata to controller/method.

#[Deprecated(
    message: 'Use v2 endpoint',
    sunsetDate: '2026-12-31',
    replacedBy: '2.0'
)]

Response Headers

When middleware is active, responses can include:

X-API-Version: 2.0
X-API-Supported-Versions: 1.0, 1.1, 2.0, 2.1
X-API-Route-Versions: 2.0, 2.1
X-API-Deprecated: true
X-API-Deprecation-Message: Use store() instead
X-API-Sunset: 2026-12-31
X-API-Replaced-By: 2.1

Versioned Resources

VersionedJsonResource

Extend VersionedJsonResource and implement toArrayDefault(). Then optionally add version-specific methods (toArrayV1, toArrayV11, toArrayV2, toArrayV21) or custom mappings via config.

use Illuminate\Http\Request;
use ShahGhasiAdil\LaravelApiVersioning\Http\Resources\VersionedJsonResource;

class UserResource extends VersionedJsonResource
{
    protected function toArrayV1(Request $request): array
    {
        return ['id' => $this->id, 'name' => $this->name];
    }

    protected function toArrayV2(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
        ];
    }

    protected function toArrayDefault(Request $request): array
    {
        return $this->toArrayV2($request);
    }
}

VersionedResourceCollection

Extend VersionedResourceCollection for version-aware list payloads.

use Illuminate\Http\Request;
use ShahGhasiAdil\LaravelApiVersioning\Http\Resources\VersionedResourceCollection;

class UserCollection extends VersionedResourceCollection
{
    protected function toArrayV1(Request $request): array
    {
        return ['data' => $this->collection];
    }

    protected function toArrayV2(Request $request): array
    {
        return [
            'data' => $this->collection,
            'meta' => ['count' => $this->collection->count()],
        ];
    }

    protected function toArrayDefault(Request $request): array
    {
        return $this->toArrayV2($request);
    }
}

Version Comparison Helpers

In controllers/resources using HasApiVersionAttributes:

  • getCurrentApiVersion()
  • isVersionDeprecated()
  • getDeprecationMessage()
  • getSunsetDate()
  • getReplacedByVersion()
  • isVersionGreaterThan()
  • isVersionGreaterThanOrEqual()
  • isVersionLessThan()
  • isVersionLessThanOrEqual()
  • isVersionBetween()

Direct service usage:

use ShahGhasiAdil\LaravelApiVersioning\Services\VersionComparator;

$comparator = app(VersionComparator::class);
$comparator->isGreaterThan('2.0', '1.0');
$comparator->satisfies('2.1', '^2.0');

Error Format (RFC 7807)

Unsupported versions return application/problem+json:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "Unsupported API Version",
  "status": 400,
  "detail": "API version '3.0' is not supported for this endpoint.",
  "requested_version": "3.0",
  "supported_versions": ["1.0", "1.1", "2.0", "2.1"],
  "endpoint_versions": ["2.0", "2.1"]
}

Optional documentation is included when api-versioning.documentation.base_url is set.

Artisan Commands

Generate controller stub with attributes:

php artisan make:versioned-controller UserController --api-version=2.0
php artisan make:versioned-controller V1UserController --api-version=1.0 --deprecated --sunset=2026-12-31 --replaced-by=2.0

Inspect routes and versions:

php artisan api:versions
php artisan api:versions --route=users
php artisan api:versions --api-version=2.0
php artisan api:versions --deprecated
php artisan api:versions --compact
php artisan api:versions --json

Check configuration health:

php artisan api:version:health

Show config overview:

php artisan api:version-config --show
php artisan api:version-config --add-version=2.2 --method=toArrayV22

Clear attribute cache:

php artisan api:cache:clear

Testing Helpers

Use ShahGhasiAdil\LaravelApiVersioning\Testing\ApiVersionTestCase in package/app tests.

Available helpers include:

  • Requests: getWithVersion, getWithVersionQuery, postWithVersion, putWithVersion, deleteWithVersion
  • Assertions: assertApiVersion, assertApiVersionDeprecated, assertApiVersionNotDeprecated, assertSupportedVersions, assertRouteVersions, assertDeprecationMessage, assertReplacedBy

Configuration Reference

Main file: config/api-versioning.php

return [
    'default_version' => '1.0',

    'detection_methods' => [
        'header' => [
            'enabled' => true,
            'header_name' => 'X-API-Version',
        ],
        'query' => [
            'enabled' => true,
            'parameter_name' => 'api-version',
        ],
        'path' => [
            'enabled' => true,
            'prefix' => 'api/v',
        ],
        'media_type' => [
            'enabled' => false,
            'format' => 'application/vnd.api+json;version=%s',
        ],
    ],

    'supported_versions' => ['1.0', '1.1', '2.0', '2.1'],

    'version_method_mapping' => [
        '1.0' => 'toArrayV1',
        '1.1' => 'toArrayV11',
        '2.0' => 'toArrayV2',
        '2.1' => 'toArrayV21',
    ],

    'version_inheritance' => [
        '1.1' => '1.0',
        '2.1' => '2.0',
    ],

    'default_method' => 'toArrayDefault',

    'documentation' => [
        'base_url' => env('API_DOCUMENTATION_URL'),
    ],

    'cache' => [
        'enabled' => env('API_VERSIONING_CACHE_ENABLED', true),
        'ttl' => env('API_VERSIONING_CACHE_TTL', 3600),
    ],
];

Environment variables:

API_DOCUMENTATION_URL=https://docs.example.com/api
API_VERSIONING_CACHE_ENABLED=true
API_VERSIONING_CACHE_TTL=3600

Development

Run tests:

composer test

Run static analysis:

composer analyse

Format code:

composer format

Changelog

Please see CHANGELOG for more information on what has changed recently.

Security Vulnerabilities

If you discover any security-related issues, please email adil.shahghasi@gmail.com.

Credits

License

The MIT License (MIT). Please see LICENSE.md for details.