Un tutoriel pour mettre en place une REST API qui retourne les données au format JSON dans un projet Laravel.
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.
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
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 index
, store
, show
, update
et destroy
.--model=User
permet d'importer et d'injecter le modèle /app/Models/User.php aux méthodes show
, update
et destroy
du contrôleurA 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.
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 |
Revenons sur les actions de routes ou méthodes du contrôleur /app/Http/Controllers/API/UserController.php :
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.
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);
}
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);
}
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();
}
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();
}
}
Voici quelques techniques pour améliorer le fonctionnement de l'API :
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);
}
});
}
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)
{
// ...
}
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.
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);
}
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);
}
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, 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 profilAutres publications
Voir toutes les publication de Wilo Ahadi
Sélection ebook
10 Laravel tips and techniques for your next PHP project
Développement web
Commentaires