Site icon Régis Enguehard

Laravel et les API

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 :

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));
});

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));
        });
    }
);
Quitter la version mobile