Les API REST
API (interface de programmation applicative) est un ensemble normalisé par laquelle une application offre des services à d’autres applications.
REST (Representational State Transfer) est un style d’architecture pour constituer des API. Laravel permet de bâtir facilement des API à partir de ressource Eloquent. Laravel va nous retourner une Collection
directement en json avec les champs voulus.
Pour tester les API, le mieux est d’utiliser un logiciel comme :
Une liste d’éléments :
GET /api/evenements [ { id: 1, nom: 'Aut numquam.' }, { id: 2, nom: 'Sint incidunt consequatur.' } ... ]
Un élément en particulier :
GET /api/evenements/2 { id: 2, name: 'Sint incidunt consequatur.' }
Il est possible également de gérer l’ajout, la modification et la suppression.
Installation
Partons d’une installation fraîche de Laravel :
$ composer create-project laravel/laravel laravel-api
Créons une base de données, et renseignons le fichier .env
.
Lançons les migrations :
$ php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (59.68ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (57.33ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (52.80ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (87.07ms)
Modifions le fichier database/seeders/DatabaseSeeder.php
afin de générer une centaine d’utilisateurs :
public function run() { \App\Models\User::factory(100)->create(); }
Puis lançons le peuplement :
$ php artisan db:seed Database seeding completed successfully.
Resource
Créons une resource pour notre API :
$ php artisan make:resource UserResource
Cela nous créer un nouveau fichier : app/Http/Resources/UserResource.php
Par défaut, la Ressource
retourne tous les champs de la table, mais nous pouvons éditer notre Ressource
pour y retourner uniquement les champs voulus :
public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'email_verified_at' => $this->email_verified_at, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; }
Routes
Pour les API, Laravel mets à disposition le fichier routes/api.php
pour spécifier toutes les routes de notre api. Celle-ci seront accessible via http://localhost:8000/api/ROUTE
:
Créons 2 routes :
- Affiche la liste des utilisateurs
- Affiche le détail d’un utilisateur
use App\Models\User; use App\Http\Resources\UserResource; [...] Route::get('/users', function () { return UserResource::collection(User::paginate(20)); }); Route::get('/user/{id}', function ($id) { return new UserResource(User::findOrFail($id)); });
http://localhost:8000/api/users
nous retournera les utilisateurs avec une paginationpaginate(20)
, on aurait pu lui dire de tout nous retournerall()
.http://localhost:8000/api/user/5
nous retournera l’utilisateur dont l’identifiant est5
.
Si l’on souhaite que les entrées json
aient le même identifiant que l’id
de la table, au niveau de la route :
Route::get('/evenements', function () { return UserResource::collection(Evenement::paginate(20)->keyBy('id')); });
et dans la Ressource
:
public $preserveKeys = true;
Collection
Nous pourrions également avoir besoin d’ajouter des informations complémentaires à nos données.
Créons une collection :
$ php artisan make:resource UserCollection
Éditons notre fichier :
public function toArray($request) { return [ 'data' => $this->collection, 'metadata' => [ 'title' => env('APP_NAME'), 'url' => env('APP_URL'), ], ]; }
Et modifions nos routes :
Route::get('/users', function () { return new UserCollection(User::paginate(20)); }); Route::get('/user/{id}', function ($id) { return new UserResource(User::findOrFail($id)); });
404 et API
Si nous cherchons une URL qui n’existe pas, par défaut, la page retournée est la page 404 par défaut, donc en HTML…
On peut la modifier en modifiant la méthode register()
du fichier /app/Exceptions/Handler.php
on va filtrer les requêtes sur les urls de types api/*
:
public function register() { $this->renderable(function (NotFoundHttpException $e, $request) { if ($request->is("api/*")) { return response()->json([ "error" => "404", "message" => "File Not Found", ], 404); } }); }
Limiter le nombre de requêtes sur l’API
Dans notre fichier api.php
, ajoutons le RateLimiter
:
use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; RateLimiter::for('global', function (Request $request) { return Limit::perMinute(100)->response(function () { return response()->json([ "error" => "429", "message" => "Too many requests", ], 429); }); });
On peut faire une limitation, par minutes perMinute
, par heures perHour
, par jour perDay
… et combiner par adresse IP return Limit::perMinute(100)->by($request->ip());
.
Il ne nous reste plus qu’à ajouter ce RateLimiter
à nos routes d’API ; toujours dans le même fichier :
Route::middleware(['throttle:global'])->group( function () { Route::get('/users', function () { return new UserCollection(User::paginate(20)); }); Route::get('/user/{id}', function ($id) { return new UserResource(User::findOrFail($id)); }); } );