bugloos/api-versioning-bundle
Installation
composer require bugloos/api-versioning-bundle
Add to config/bundles.php:
return [
// ...
Bugloos\ApiVersioningBundle\BugloosApiVersioningBundle::class => ['all' => true],
];
Basic Configuration Publish the default config:
php artisan vendor:publish --provider="Bugloos\ApiVersioningBundle\BugloosApiVersioningBundle" --tag="config"
Edit config/api_versioning.php to define your versioning strategy (e.g., header, query, or path).
First Use Case
Annotate a controller method with @ApiVersion("1") to enforce versioning:
use Bugloos\ApiVersioningBundle\Annotation\ApiVersion;
class UserController extends AbstractController
{
/**
* @ApiVersion("1")
*/
public function getUser(Request $request)
{
// Version 1 logic
}
}
Testing
Use the API_VERSION header or query param (?version=1) to test versioned endpoints.
Header-Based (Recommended):
Set API_VERSION header (e.g., API_VERSION: 1).
Configure in config/api_versioning.php:
'strategy' => Bugloos\ApiVersioningBundle\Strategy\HeaderStrategy::class,
Query Parameter:
Use ?version=1 in URLs.
Configure:
'strategy' => Bugloos\ApiVersioningBundle\Strategy\QueryStrategy::class,
Path-Based:
Use /v1/users (requires route prefixing; not natively supported by this bundle—see Gotchas).
Versioned Controllers: Group versioned logic in separate controllers or methods:
class V1UserController extends AbstractController
{
/**
* @ApiVersion("1")
*/
public function index() { /* V1 logic */ }
}
class V2UserController extends AbstractController
{
/**
* @ApiVersion("2")
*/
public function index() { /* V2 logic */ }
}
Middleware Integration: Use the bundle’s middleware to enforce versioning globally:
// config/routes.php
$kernel->addControllerMiddleware(new ApiVersioningMiddleware());
Dynamic Version Switching:
Combine with Symfony’s Request to dynamically load versioned services:
$version = $this->get('api_versioning.version_resolver')->getVersion();
$service = $this->get(sprintf('app.user_service.%s', $version));
Documentation:
Use @ApiVersion in PHPDoc to auto-generate API docs (e.g., Swagger/OpenAPI plugins).
API Platform:
Extend ApiResource to include versioning metadata:
#[ApiVersion("1")]
class User extends ApiResource { ... }
Event Listeners: Trigger version-specific events:
$this->get('event_dispatcher')->dispatch(
new VersionRequestedEvent($version, $request)
);
Caching: Cache responses per version:
$cacheKey = sprintf('api_v%s_%s', $version, $request->getPathInfo());
Path-Based Versioning Limitation:
The bundle does not natively support /v1/endpoint routing. Workaround:
public function handle(Request $request, Closure $next)
{
$version = $request->attributes->get('version');
if ($version) {
$request->headers->set('API_VERSION', $version);
}
return $next($request);
}
Register in routes.php:
$kernel->addControllerMiddleware(new ExtractVersionFromPathMiddleware());
Annotation Override:
@ApiVersion on a method overrides class-level annotations. Test this behavior early.
Symfony 5.4+ Conflicts:
If using Symfony 5.4+, ensure api_platform.core.eventlistener is not overriding versioning logic.
Default Version Fallback:
The bundle throws a VersionNotFoundException if no version is provided. Configure a default in config/api_versioning.php:
'default_version' => '1',
Check Resolved Version: Log the resolved version in middleware:
$version = $this->get('api_versioning.version_resolver')->getVersion();
\Log::debug('Resolved API version:', ['version' => $version]);
Validate Headers:
Ensure API_VERSION header is correctly set (case-sensitive). Use Postman/cURL to test:
curl -H "API_VERSION: 1" http://your-api/users
Clear Cache: After config changes, run:
php artisan cache:clear
php artisan config:clear
Versioned Routes:
Use Symfony’s requirements to enforce versioning in routing:
# config/routes.yaml
_api:
path: /api
controller: Bugloos\ApiVersioningBundle\Controller\ApiVersioningController::indexAction
requirements:
version: \d+
Deprecation Warnings: Log warnings for deprecated versions:
if ($version === '1' && $request->headers->get('X-Request-Id')) {
\Log::warning('Version 1 is deprecated', ['request_id' => $request->headers->get('X-Request-Id')]);
}
Testing:
Use ApiVersioningBundle\Tests\VersionResolverTest as a reference for writing tests:
$this->client->request('GET', '/users', [], [], [
'HTTP_API_VERSION' => '1',
]);
Extension Points:
Bugloos\ApiVersioningBundle\Strategy\AbstractStrategy for custom version sources (e.g., JWT claims).api_versioning.version_resolved to modify version logic dynamically.Performance:
Cache the VersionResolver service if resolving versions frequently:
$this->container->get('api_versioning.version_resolver')->setCache($cache);
How can I help you explore Laravel packages today?