Skip to main content
Category:

Versioning your REST API with Laravel

 

API versioning has become very common in recent years, the main idea behind API versions is to never delete the old versions of our code  when we need to make big changes in our system, mainly when this changes could create problems with integrations (Softwares who are consuming our APIs). Following this idea we will have endpoints like this:

  • /api/v1/users
  • /api/v2/users

Note that the difference between the endpoints is “v1” and “v2”, they are related to the version we are using. On the way, the old integrations (apps, external Softwares, Front-end applications,…) could keep using the first version (v1), the new version integrations could just use version 2 (v2). Following this principle, we nevermore will worry about updates or new features.

 

Applying versioning to folders

In the following examples, I will show how I use to versioning my APIs.

The first thing to do is to organize the folders. Create a folder called Api inside app/Http/Controllers, after, create do two folders called V1 and V2 inside app/Http/Controllers/Api, you should have something like this:

  • app/Http/Controllers/Api/V1
  • app/Http/Controllers/Api/V2

 

/app
  /controllers
    /Api
      /v1
        /UserController.php
      /v2
        /UserController.php

 

Example how to create controllers for v1 version:

# php artisan make:controller Api/V1/MyController

Example how to create jsonResource for v1 version

# php artisan make:resource V1/UserResource

Example how to create jsonResource for v2 version

# php artisan make:resource V2/UserResource

 

The idea is that each controller uses a different resource, the first controller version should look like this:

<?php
namespace App\Http\Controllers\Api\V1;

use App\Models\User;

class UserController
{
    public function getUser(int $idUser)
    {
        $user = User::find($idUser);
        
        return new \App\Http\Resources\V1\User($user);
    }
}

In the second version, we just need to use a different resource. The same idea could be replicated for Models, Views, Services, Repositories, or whatever layer you could be using.

 

Loading the controllers according to the version (URL)

To do that we will use the Laravel middleware. First of all, open the file config/app.php in your favorite editor, add the following lines of code:

/*
|-------------------------------------------
| Current API Version
|-------------------------------------------
*/

'api_latest'  => '2',

 

We need to create our middleware now. Execute the following command on your terminal:

php artisan make:middleware APIVersion

Open the middleware created (app\Http\Middleware\APIVersion.php) and add the code:

<?php
namespace App\Http\Middleware;use Closure;

/**
 * Class APIVersion
 * @package App\Http\Middleware
 */
class APIVersion
{
    /**
     * Handle an incoming request.
     *
     * @param  Request $request
     * @param  Closure $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next, $guard)
    {
        config(['app.api.version' => $guard]);
        return $next($request);
    }
}

This middleware will be responsible for intercepting the requests and apply the version requested by the clients.

Don't forget to register it on your app/Http/Kernel.php file:

protected $routeMiddleware = [
    // ...
    'api_version' => App\Http\Middleware\APIversion::class,
];

Open the file ../app/Providers/RouteServiceProvider.php and add this attribute to the class:

/** @var string $apiNamespace */
protected $apiNamespace ='App\Http\Controllers\Api';

 

Inside the method mapApiRoutes, add the following code:

/**
 * Define the "api" routes for the application.
 *
 * These routes are typically stateless.
 *
 * @return void
 */
protected function mapApiRoutes()
{
    Route::group([
        'middleware' => ['api', 'api_version:v1'],
        'namespace'  => "{$this->apiNamespace}\V1",
        'prefix'     => 'api/v1',
    ], function ($router) {
        require base_path('routes/api_v1.php');
    });        

    Route::group([
        'middleware' => ['api', 'api_version:v2'],
        'namespace'  => "{$this->apiNamespace}\V2",
        'prefix'     => 'api/v2',
    ], function ($router) {
        require base_path('routes/api_v2.php');
    });
}

In that way, we will separate the route groups in different files, look closely! we are using the PHP method require to load:

  • routes/api_v1.php
  • routes/api_v2.php

 

It will be necessary to create two different files manually after you just need to follow the same idea on the file routes/api.php created by Laravel on the setup of new projects.

The original file will not be necessary since now. I prefer this separation because it keeps the routes more organized when your project grows. After that, you just need to create your controllers and routes.

In this short article, you learned how to keep your APIs/projects more organized, mainly thinking about the future of your application. Feel free to make changes and improve it, then, improving the quality of your code.

 

 

 

Source of this article

Riadh Rahmi

Senior Web Developer PHP/Drupal & Laravel

I am a senior web developer, I have experience in planning and developing large scale dynamic web solutions especially in Drupal & Laravel.

Web Posts

Search

Page Facebook