Pourquoi Laravel avec GraphQL ?
L'adoption de GraphQL dans une application Laravel peut offrir des avantages significatifs, notamment en termes de performance et d'efficacité de développement. En effet, plutôt que de récupérer un ensemble fixe de données lors d'une requête HTTP, GraphQL permet aux clients de spécifier exactement ce qu'ils veulent recevoir, réduisant ainsi le temps de réponse et améliorant la qualité des données renvoyées.
Un cas d'usage concret est une application mobile qui affiche des informations sur un produit. Au lieu de récupérer toutes les propriétés d'un produit lors d'une requête HTTP standard, l'application mobile peut spécifier que elle veut uniquement le nom, le prix et la description du produit. Avec GraphQL, cela peut être fait en une seule requête, permettant à l'application de récupérer précisément ce qu'elle a besoin.
Prerequis
- Connaissance de base de Laravel (version 8 ou supérieure recommandée)
- Connaissance des bases de données SQL
- Connaissance du langage PHP
- Installation de Composer et Node.js
- Un serveur local comme XAMPP, WAMP ou MAMP
Concepts fondamentaux
1. Schema GraphQL
Le schema de GraphQL est une description détaillée de toutes les requêtes et mutations disponibles pour l'API. Il définit le format des données qui peuvent être récupérées et comment elles sont structurées.
Exemple de schéma en PHP :
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
$schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'user' => [
'type' => Type::string(),
'args' => [
'id' => ['type' => Type::nonNull(Type::int())],
],
'resolve' => function ($root, $args) {
// Récupération des données de l'utilisateur
return "Utilisateur avec ID {$args['id']}";
},
],
],
]),
]);
2. Resolver
Un resolver est une fonction qui définit comment récupérer les données pour une requête ou une mutation. Il prend comme arguments le root objet, les arguments de la requête et les contexte.
Exemple de resolver en PHP :
$resolveFunction = function ($root, $args) {
// Logique pour récupérer les données
return "Données récupérées";
};
3. Mutations
Les mutations sont des requêtes qui modifient l'état du serveur. Elles permettent d'effectuer des opérations comme la création, la mise à jour ou la suppression de données.
Exemple de mutation en PHP :
$mutationType = new ObjectType([
'name' => 'Mutation',
'fields' => [
'createUser' => [
'type' => Type::string(),
'args' => [
'name' => ['type' => Type::nonNull(Type::string())],
'email' => ['type' => Type::nonNull(Type::string())],
],
'resolve' => function ($root, $args) {
// Logique pour créer un utilisateur
return "Utilisateur créé";
},
],
],
]);
Mise en pratique : projet fil rouge
Nous allons construire une application simple de gestionnaire de tâches. L'application permettra aux utilisateurs de récupérer, ajouter et mettre à jour des tâches.
Étape 1 : Création du projet Laravel
composer create-project --prefer-dist laravel/laravel task-manager
cd task-manager
Étape 2 : Installation d'GraphQL dans Laravel
composer require webonyx/graphql-php
Étape 3 : Configuration de la base de données
Modifier le fichier .env pour configurer votre base de données.
Exemple de configuration :
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=task_manager
DB_USERNAME=root
DB_PASSWORD=
Créer la table tasks dans la base de données.
Migration :
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateTasksTable extends Migration
{
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('completed')->default(false);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('tasks');
}
}
Exécuter la migration :
php artisan migrate
Étape 4 : Création des modèles et contrôleurs
Créer un modèle Task.
php artisan make:model Task
Modèle Task.php :
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
protected $fillable = ['title', 'description', 'completed'];
}
Créer un contrôleur pour les tâches.
php artisan make:controller TaskController --api
Contrôleur TaskController.php :
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\QueryType;
class TaskController extends Controller
{
public function index()
{
return response()->json(Task::all());
}
public function show($id)
{
$task = Task::find($id);
if (!$task) {
return response()->json(['error' => 'Task not found'], 404);
}
return response()->json($task);
}
public function store(Request $request)
{
$task = Task::create($request->all());
return response()->json($task, 201);
}
public function update(Request $request, $id)
{
$task = Task::find($id);
if (!$task) {
return response()->json(['error' => 'Task not found'], 404);
}
$task->update($request->all());
return response()->json($task);
}
public function destroy($id)
{
$task = Task::find($id);
if (!$task) {
return response()->json(['error' => 'Task not found'], 404);
}
$task->delete();
return response()->json(null, 204);
}
}
Étape 5 : Configuration de la route GraphQL
Créer un fichier graphql.php dans le répertoire config.
Fichier config/graphql.php :
return [
'schema' => 'Query',
];
Modifier le fichier routes/api.php pour ajouter une route GraphQL.
Route routes/api.php :
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TaskController;
Route::get('/graphql', function () {
return \GraphQL\HTTP\query(
(new \GraphQL\Schema\Schema(['query' => new QueryType])),
null,
null,
null,
request()->all()
);
});
Étape 6 : Création du schéma GraphQL
Créer un fichier schema.php dans le répertoire app/GraphQL.
Fichier app/GraphQL/schema.php :
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
$schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'tasks' => [
'type' => Type::listOf(Type::string()),
'resolve' => function () {
return Task::all()->map->only(['id', 'title', 'description'])->toArray();
},
],
'task' => [
'type' => Type::string(),
'args' => [
'id' => ['type' => Type::nonNull(Type::int())],
],
'resolve' => function ($root, $args) {
return Task::find($args['id'])->only(['id', 'title', 'description']);
},
],
],
]),
]);
return $schema;
Étape 7 : Ajout des resolvers
Ajouter des resolvers pour les mutations.
Fichier app/GraphQL/mutations.php :
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\MutationType;
$mutationType = new MutationType([
'name' => 'Mutation',
'fields' => [
'createTask' => [
'type' => Type::string(),
'args' => [
'title' => ['type' => Type::nonNull(Type::string())],
'description' => ['type' => Type::string()],
],
'resolve' => function ($root, $args) {
Task::create($args);
return "Task created";
},
],
'updateTask' => [
'type' => Type::string(),
'args' => [
'id' => ['type' => Type::nonNull(Type::int())],
'title' => ['type' => Type::string()],
'description' => ['type' => Type::string()],
],
'resolve' => function ($root, $args) {
Task::find($args['id'])->update($args);
return "Task updated";
},
],
'deleteTask' => [
'type' => Type::string(),
'args' => [
'id' => ['type' => Type::nonNull(Type::int())],
],
'resolve' => function ($root, $args) {
Task::find($args['id'])->delete();
return "Task deleted";
},
],
],
]);
$schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'tasks' => [
'type' => Type::listOf(Type::string()),
'resolve' => function () {
return Task::all()->map->only(['id', 'title', 'description'])->toArray();
},
],
'task' => [
'type' => Type::string(),
'args' => [
'id' => ['type' => Type::nonNull(Type::int())],
],
'resolve' => function ($root, $args) {
return Task::find($args['id'])->only(['id', 'title', 'description']);
},
],
],
]),
'mutation' => $mutationType,
]);
return $schema;
Étape 8 : Test de l'API GraphQL
Lancer le serveur local et accéder à http://localhost:8000/graphql pour tester les requêtes.
Requête pour obtenir toutes les tâches :
query {
tasks {
id
title
description
}
}
Requête pour créer une nouvelle tâche :
mutation {
createTask(title: "Nouvelle Tâche", description: "Description de la nouvelle tâche") {
result
}
}
Erreurs frequentes et debugging
1. Call to undefined function GraphQL\GraphQL::schema()
Le problème peut être causé par une version incompatble de graphql-php.
Code incorrect :
use GraphQL\GraphQL;
Correction :
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
2. Call to undefined method App\Models\Task::only()
Le problème peut être causé par une mauvaise utilisation de la méthode only.
Code incorrect :
return Task::find($args['id'])->only(['id', 'title', 'description']);
Correction :
return Task::find($args['id'])->getAttributes();
3. Non-scalar return type hinting is not allowed for a public method in class App\Http\Controllers\TaskController
Le problème peut être causé par une mauvaise définition des types de retour dans les méthodes du contrôleur.
Code incorrect :
public function index()
{
return Task::all();
}
Correction :
public function index()
{
return response()->json(Task::all());
}
Pour aller plus loin
Intégration avec Apollo Client pour le frontend
Utilisation de GraphQL Shield pour la gestion des autorisations
Optimisation des performances avec DataLoader
Défi pratique
Créer une application complète de gestion de projets utilisant GraphQL. L'application devrait permettre de créer, lire, mettre à jour et supprimer des projets et leurs tâches associées.