What it means to see an Apirestful ?
Imagine the following situation, you are developing an API that will be consumed by an application developed for IOS/Android, one of the endpoints of your API aims to return a list of system users to show on the main screen of the App, the URL to consume this user listing is "GET https://www.meu-site-exemplo.com.br/api/usuarios
" and the return of that endpoint is:
[{
"nome": "Usuário de teste 1",
"email": "Email de teste 1",
"telefone": "+353832123323233"
},{
....
}]
The developer responsible for the App finishes the integration and makes it available for download in the Appstore and Google Play, after that, many people download and are using it on their mobile phones. One fine day your company is sold to an American company and for some reason they want to remodel the App and standardize the API to return the name of the fields in English, at first glance all this seems very simple, just change the return of the API and update the App to read these new fields:
[{
"name": "Usuário de teste 1",
"email": "Email de teste 1",
"phone": "+353832123323233"
},{
....
}]
All who download the new version of the App will continue using perfectly, but we have a big problem, not everyone who was already using it before will update it automatically. When they try to use it, the App will make a request to "GET https://www.meu-site-exemplo.com.br/api/usuarios
" and return the list of users with the fields in English, but the App will be waiting fields in Portuguese, and that is where we will have a big problem.
I believe this is the most common problem of who is developing an API that will be consumed by Apps, we also have many other examples. In 2014 I worked on an E-commerce platform in the integration team, our role was every day to integrate the platform with dozens of different Apis/Webservices, we often suffered from this problem, the external services simply changed something and everything stopped working, simply the chaos on earth.
The Solution:
The versioning of Apis has become very common in recent years, the idea is to never erase the old code (logic) when we need to make big changes or changes that can break the integrations, add new fields maybe it is not a problem, but to change those that exist. Following this logic, we could create such endpoints:
https://www.meu-site-exemplo.com.br/api/v1/usuarios
https://www.meu-site-exemplo.com.br/api/v2/usuarios
Note that the difference is v1 and v2 which concern the version being accessed. That way the old integrations could continue using the first version and the new ones would use v2 ,building the code in an organized way, we would never have to worry about it again.
Applying versioning to directories
In the following examples I will demonstrate how I usually use my Apis, but I know that there are different approaches to doing this.
The first part is to organize our directories inside the folder app/Http/Controllers
, create an Api folder inside and within it create two v1 and v2 folders, this way we will have the following directories:
app/Http/Controllers/Api/V1
app/Http/Controllers/Api/V2
we can also create these folders directly when we create our controllers via the command:
php artisan make:controller Api/V1/MinhaController
This way it will already create the directories and class with the correct namespace.
We can also select our Resources to have different returns, just run in the creation of a Resource or Collection the following commands:
php artisan make:resource V1/UserResource
or
php artisan make:resource V2/UserResource
and within each of the controllers I use its respective Resource, as an example of a controller using the first version of our API:
<?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 version 2 controller, just use a different Source. This same template can be used for Models, Views, Services, Repositories, or any other structure you are using, including for testing.
But we still have a problem, how does Laravel redirect when you put v1 or v2 in the url? how does it call the correct controller?
Loading controllers according to URL version
To do this we will use the Laravel Middleware, first access the file config/app.php
and add the following code in the last lines:
/*
|-------------------------------------------
| Versão da API
|-------------------------------------------
|
| Essa é a ultima versão de sua API, caso não seja informado a | versão desejada, essa será utilizada.
*/
'api_latest' => '2',
Now we will create our middleware, execute the following command on your terminal:
php Artisan make:middleware Apiversion
Open the created file (app\Http\Middleware\APIVersion.php
) and insert the following 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 request and applying the version we are calling for (if we do not inform the version). Access the file ../app/Providers/RouteServiceProvider.php
and add the following attribute to the class:
/** @var string $apiNamespace */
protected $apiNamespace ='App\Http\Controllers\Api';
within the mapApiRoutes method insert 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');
});
}
This way I am separating the groups of routes by different files, notice that in the require methods I am loading:
routes/api_v1.php
routes/api_v2.php
you will need to create these two files manually and follow the same file logic that is already created by Laravel (routes/api.php
), this original file will no longer be used. I prefer this separation because it keeps things much more organized, after that just create your routes pointing to each controller.
This reply was taken from the article Versioning your Apirestful with Laravel
Create an app for each version. Usually when there is a change that compromises the operation of a previous version.
– Daniel Omine
You can use GIT to make your life easier: https://git-scm.com/doc
– JuniorNunes