Laravel : Créer une API REST

Mis à jour il y a 1 an

Un tutoriel pour mettre en place une REST API qui retourne les données au format JSON dans un projet Laravel.

Wilo Ahadi

Auteur

Wilo A.

Technologies

Laravel, PHP
Voir aussi Un tutoriel pour utiliser la librairie Curl de PHP qui permet de se connecter et communiquer avec un serveur distant depuis votre application En savoir plus

Introduction au REST API

Pour les applications web, une API (Application Programming Interface) ou « Interface de Programmation d'Applications » en français, est un ensemble de définitions et de protocoles qui permettent à des applications de communiquer et de s'échanger mutuellement des données ou des services.

REST (Representational State Transfert) est un style d'architecture logicielle définissant un ensemble de contraintes à utiliser pour créer un service web.

JSON (JavaScript Object Notation) est un format standard qui permet de représenter des données structurées de façon semblable aux objets JavaScript. Exemple :

[
    {
        "id": 1,
        "name": "Jeremie Wilondja",
        "email": "[email protected]",
        "created_at": "2021-10-23T16:31:33.000000Z"
    },
    {
        "id": 2,
        "name": "Wilo Ahadi",
        "email": "[email protected]",
        "created_at": "2021-10-23T18:24:18.000000Z"
    }
]

On parle d'API REST ou REST API lorsqu'une API respecte les contraintes architecturales de REST.

Nous voulons voir dans ce guide comment mettre en place une API REST qui retourne les données au format JSON dans un projet Laravel.

Pour mettre en place ce système, nous allons commencer par voir les prérequis pour l'API, ensuite le contrôleur de l'API, définir les routes de l'API puis compléter les méthodes du contrôleur.

Prérequis pour l'API

Nous supposons que vous disposez déjà d'un projet Laravel, sinon référez-vous à ce tutoriel :

👉 Créer un projet Laravel avec Laragon

Pour l'API que nous allons mettre en place, nous avons besoin de données. Utilisons la table existante « users » dont le schéma est décrit dans la méthode up() de la migration /databases/migrations/..._create_users_table.php :

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

Pour importer (migrer) la table « users » dans la base de données, on exécute la commande artisan suivante :

php artisan migrate

Le contrôleur de l'API

Pour générer le contrôleur de l'API, appelons-le « UserController », exécutons la commande artisan suivante :

php artisan make:controller API/UserController --model=User --api

Les options :

  • --api permet de retirer les méthodes create et edit du contrôleur puisqu'elles ne sont pas utiles pour une API. Il va nous rester indexstoreshowupdate et destroy.
  • --model=User permet d'importer et d'injecter le modèle /app/Models/User.php aux méthodes showupdate et destroy du contrôleur

A ce niveau, nous avons le code suivant au fichier app/Http/Controllers/API/UserController.php généré :

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index() { }

    public function store(Request $request) { }

    public function show(User $user) { }

    public function update(Request $request, User $user) { }

    public function destroy(User $user) { }
}

Nous allons compléter les méthodes de ce contrôleur après avoir vu les routes.

Les routes de l'API

Pour définir les routes « users.* » (users.index, users.show, users.store, ...) de l'API, appelons la méthode Route::apiRessource() au fichier /routes/api.php :

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

use App\Http\Controllers\API\UserController;

Route::apiResource("users", UserController::class); // Les routes "users.*" de l'API

Pour afficher les routes générées, on exécute la commande artisan suivante dans la console :

php artisan route:list

Voici les routes que nous avons :

Method URI Name Action
GET|HEAD api/users users.index App\Http\Controllers\API\UserController@index
POST api/users users.store App\Http\Controllers\API\UserController@store
GET|HEAD api/users/{user} users.show App\Http\Controllers\API\UserController@show
PUT|PATCH api/users/{user} users.update App\Http\Controllers\API\UserController@update
DELETE api/users/{user} users.destroy App\Http\Controllers\API\UserController@destroy

Les méthodes du contrôleur de l'API

Revenons sur les actions de routes ou méthodes du contrôleur /app/Http/Controllers/API/UserController.php :

1. L'action index

La méthode index() de la route nommée « users.index » permet d'afficher une liste de la ressource :

Method URI Name Action
GET|HEAD api/users users.index App\Http\Controllers\API\UserController@index

La ressource dont nous parlons ici est /app/Models/User.php. On récupère tous les utilisateurs $users puis on retourne les informations en réponse JSON :

public function index()
{
    // On récupère tous les utilisateurs
    $users = User::all();

    // On retourne les informations des utilisateurs en JSON
    return response()->json($users);
}

La méthode response()->json($data, $code) où $data représente le body (corps) et $code le code de statut de la réponse HTTP, retourne une réponse avec le header « Content-Type : applicaton/json ».

Nous obtenons le code de statut de réponse « 200 OK » lorsqu'une requête réussit et « 404 Not Found » lorsque la ressource demandée n'est pas trouvée.

2. L'action store

Le méthode store(Request $request) de la route nommée « users.store » permet d'enregistrer une nouvelle ressource :

Method URI Name Action
POST api/users users.store App\Http\Controllers\API\UserController@store

On valide les données de la requête, ensuite on insère un nouvel utilisateur dans la table « users » puis on retourne une réponse JSON avec les informations du $user nouvellement créé et le code « 201 Created » :

public function store(Request $request)
{
    // La validation de données
    $this->validate($request, [
        'name' => 'required|max:100',
        'email' => 'required|email|unique:users',
        'password' => 'required|min:8'
    ]);

    // On crée un nouvel utilisateur
    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => bcrypt($request->password)
    ]);

    // On retourne les informations du nouvel utilisateur en JSON
    return response()->json($user, 201);
}

3. L'action show

La méthode show(User $user) de la route nommée « users.show » permet d'afficher une ressource spécifiée :

Method URI Name Action
GET|HEAD api/users/{user} users.show App\Http\Controllers\API\UserController@show

On récupère un utilisateur $user de la base de données puis on retourne ses informations en réponse JSON :

public function show(User $user)
{
    // On retourne les informations de l'utilisateur en JSON
    return response()->json($user);
}

4. L'action update

La méthode update(User $user) de la route nommée « users.update » permet de mettre à jour une ressource spécifiée :

Method URI Name Action
PUT|PATCH api/users/{user} users.update App\Http\Controllers\API\UserController@update

On valide les données de la requête, ensuite on modifie les informations de l'utilisateur $user puis on retourne une réponse JSON :

public function update(Request $request, User $user)
{
    // La validation de données
    $this->validate($request, [
        'name' => 'required|max:100',
        'email' => 'required|email',
        'password' => 'required|min:8'
    ]);

    // On modifie les informations de l'utilisateur
    $user->update([
        "name" => $request->name,
        "email" => $request->email,
        "password" => bcrypt($request->password)
    ]);

    // On retourne la réponse JSON
    return response()->json();
}

5. L'action destroy

La méthode destroy(User $user) de la route nommée « users.destroy » permet de supprimer une ressource spécifiée :

Method URI Name Action
DELETE api/users/{user} users.destroy App\Http\Controllers\API\UserController@destroy

On récupère un utilisateur $user à partir de son identifiant, ensuite on le supprime puis on retourne une réponse JSON :

public function destroy(User $user)
{
    // On supprime l'utilisateur
    $user->delete();

    // On retourne la réponse JSON
    return response()->json();
}

Nous venons de terminer l'implémentation de l'API. Nous allons voir comment l'améliorer au point suivant. A ce niveau, le code source complet du contrôleur /app/Http/Controllers/API/UserController.php est :

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index()
    {
        // On récupère tous les utilisateurs
        $users = User::all();

        // On retourne les informations des utilisateurs en JSON
        return response()->json($users);
    }

    public function store(Request $request)
    {
        // La validation de données
        $this->validate($request, [
           'name' => 'required|max:100',
           'email' => 'required|email|unique:users',
           'password' => 'required|min:8'
        ]);

        // On crée un nouvel utilisateur
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password)
        ]);

        // On retourne les informations du nouvel utilisateur en JSON
        return response()->json($user, 201);
    }

    public function show(User $user)
    {
        // On retourne les informations de l'utilisateur en JSON
        return response()->json($user);
    }

    public function update(Request $request, User $user)
    {
        // La validation de données
        $this->validate($request, [
           'name' => 'required|max:100',
           'email' => 'required|email',
           'password' => 'required|min:8'
        ]);

        // On modifie les informations de l'utilisateur
        $user->update([
            "name" => $request->name,
            "email" => $request->email,
            "password" => bcrypt($request->password)
        ]);

        // On retourne la réponse JSON
        return response()->json();
    }

    public function destroy(User $user)
    {
        // On supprime l'utilisateur
        $user->delete();

        // On retourne la réponse JSON
        return response()->json();
    }
}

Améliorer l'API

Voici quelques techniques pour améliorer le fonctionnement de l'API :

1. Modifier la réponse 404

En cas de réponse « 404 Not Found », la page 404 par défaut de Laravel est retournée ; chose qui n'est pas pratique pour une API. Modifions ce comportement en retournant une réponse JSON à la place de cette page 404 par défaut.

Nous pouvons personnaliser le rendu de l'exception NotFoundHttpException pour les requêtes « /api/* » à travers la méthode renderable() au fichier /app/Exceptions/Handler.php :

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; // Ne pas oublier d'importer NotFoundHttpException

// ...

public function register()
{
    // On modifier le comportement pour l'exception NotFoundHttpException
    $this->renderable(function (NotFoundHttpException $e, $request) {
        // Si la requête contient "api/*"
        if ($request->is("api/*")) {
            // On retourne une réponse 404 avec un message en JSON
            return response()->json([
                "message" => "Ressource introuvable"
            ], 404);
        }
    });
}

2. Utiliser une requête de formulaire

Pour valider la requête aux actions store(Request $request) et update(Request $request, User $user), nous pouvons utiliser une seule requête de formulaire « UserStoreRequest » que nous pouvons générer en exécutant la commande artisan suivante :

php artisan make:request UserStoreRequest

Ramenons la validation au fichier /app/Http/Requests/UserStoreRequest.php généré :

<?php

namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;

class UserStoreRequest extends FormRequest
{
    public function rules()
    {
        $user_id = $this->user->id ?? null; // L'indentifiant de l'utilisateur
        return [
            "name" => "required|string|max:100",
            "email" => "required|unique:users".(isset($user_id) ? ",email,".$user_id : ""),
            "password" => "required|min:8"
        ];
    }
}

Cette requête de formulaire peut être utilisée de la manière suivante :

use App\Http\Requests\UserStoreRequest; // Ne pas oublier

public function store(UserStoreRequest $request)
{
    // ...
}

public function update(UserStoreRequest $request, User $user)
{
    // ...
}

3. Utiliser une ressource d'API

Nous pouvons utiliser une ressource d'API pour transformer facilement un modèle ou une collection de modèles en réponse JSON. Retouchons les méthodes show() et index() en intégrant une ressource d'API.

L'action show

Pour la méthode show(User $user), nous pouvons générer une ressource en exécutant la commande artisan suivante :

php artisan make:resource UserResource

Cela crée le fichier app/Http/Resources/UserResource.php où nous pouvons définir les attributs à retourner pour le modèle User à travers la méthode toArray($request) :

<?php

namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray($request)
    {
        // return parent::toArray($request);
        // On retourne uniquement "name" et "email"
        return [
            "name" => ucfirst($this->name), // La 1er lettre en majuscule
            "email" => $this->email
        ];
    }
}

Nous pouvons ensuite retourner une réponse JSON au contrôleur app/Http/Controllers/API/UserController.php de la manière suivante :

use App\Http\Resources\UserResource; // Ne pas oublier

public function show(User $user)
{
    // On retourne les informations de l'utilisateur en JSON
    return new UserResource($user);
}

L'action index

Pour la méthode index(), nous pouvons retourner une collection de ressources en JSON en appelant la méthode UserResource::collection($data) :

public function index()
{
    // On récupère tous les utilisateurs
    $users = User::paginate(10);

    // On retourne les informations des utilisateurs en JSON
    return UserResource::collection($users);
}

4. Implémenter l'authentification

Le framework Laravel propose les packages officiels Passport et Sanctum qui permettent de mettre en place l'authentification d'API (OAuth2, tokens d'API, ...). Nous en parlerons dans un autre tutoriel.

En implémentant les trois premières techniques pour améliorer l'API, le code source complet du contrôleur /app/Http/Controllers/API/UserController.php devient :

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;

use App\Http\Requests\UserStoreRequest;

use App\Http\Resources\UserResource;

class UserController extends Controller
{
    public function index()
    {
        // On récupère tous les utilisateurs
        $users = User::paginate(10);

        // On retourne les informations des utilisateurs en JSON
        return UserResource::collection($users);
    }

    public function store(UserStoreRequest $request)
    {
        // On crée un nouvel utilisateur
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password)
        ]);

        // On retourne les informations du nouvel utilisateur en JSON
        return response()->json($user, 201);
    }

    public function show(User $user)
    {
        // On retourne les informations de l'utilisateur en JSON
        return new UserResource($user);
    }

    public function update(UserStoreRequest $request, User $user)
    {
        // On modifie les informations de l'utilisateur
        $user->update([
            "name" => $request->name,
            "email" => $request->email,
            "password" => bcrypt($request->password)
        ]);

        // On retourne la réponse JSON
        return response()->json();
    }

    public function destroy(User $user)
    {
        // On supprime l'utilisateur
        $user->delete();

        // On retourne la réponse JSON
        return response()->json();
    }
}

Portez-vous bien ! 😊

Cette publication vous a plu ?
Partagez-la avec vos ami(e)s sur les réseaux sociaux.

Wilo Ahadi

Wilo Ahadi, l'auteur

Passionné de l'informatique, je suis spécialiste en techniques des systèmes et réseaux, développeur web et mobile, infographiste et designer, ... J'aime partager mon expérience en formant sur la plateforme Akili School

Voir profil

Commentaires