Pourquoi Vue avec GraphQL ?
L'adoption de Vue.js avec GraphQL est une pratique courante, notamment dans les applications modernes où la performance et l'évolutivité sont des priorités. Les développeurs peuvent tirer parti de ces technologies pour construire des interfaces utilisateur réactives et efficaces en utilisant un langage de requêtes puissant pour récupérer uniquement les données nécessaires du serveur.
Un cas concret est une application e-commerce où on a besoin de récupérer des détails produits dynamiques, comme la description et les images, ainsi que leurs prix. Avec GraphQL, le client peut demander exactement ces informations, évitant ainsi un surcharge de données inutiles et réduisant le temps de réponse.
Prerequis
- Connaissances en JavaScript ES6+
- Familiarité avec Vue.js 2 ou 3
- Compréhension des bases de l'API REST
- Accès à un environnement de développement (Node.js)
- Installation de Git pour versionner le code
Outils à installer
Pour ce tutoriel, nous aurons besoin des outils suivants :
## Node.js v14+
npm install -g @vue/cli@latest
Concepts fondamentaux
1. GraphQL
GraphQL est un langage de requêtes et d'informations pour les APIs. Il permet aux clients d'interroger exactement ce qu'ils ont besoin, sans avoir à récupérer des données supplémentaires.
// Schema GraphQL
type Query {
hello: String
}
2. Client GraphQL
Un client GraphQL est un outil qui permet de faire des requêtes vers le serveur GraphQL et de gérer la réponse. Vue Apollo est l'un des clients les plus populaires avec Vue.js.
// Installation du client Apollo avec Vue
npm install vue-apollo graphql apollo-client apollo-link-http apollo-cache-inmemory graphql-tag
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-client';
import { createApolloProvider } from '@vue/apollo-option';
const httpLink = new HttpLink({
// URL de l'API GraphQL
uri: 'http://localhost:4000/graphql',
});
const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
});
3. Queries GraphQL
Une query est une requête HTTP qui récupère des données du serveur. Elle retourne uniquement les champs demandés.
// Une query pour récupérer un utilisateur par son ID
const GET_USER_QUERY = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
4. Mutations GraphQL
Une mutation est une requête HTTP qui modifie les données du serveur, comme la création d'un utilisateur.
// Une mutation pour créer un utilisateur
const CREATE_USER_MUTATION = gql`
mutation CreateUser($name: String!, $email: String!) {
createUser(name: $name, email: $email) {
id
name
email
}
}
`;
5. Subscriptions GraphQL
Une subscription est une requête HTTP qui permet de recevoir des mises à jour en temps réel du serveur.
// Une subscription pour recevoir les nouvelles tâches
const NEW_TASK_SUBSCRIPTION = gql`
subscription NewTask {
newTask {
id
title
description
}
}
`;
Mise en pratique : projet fil rouge
Nous allons créer un gestionnaire de tâches simple avec Vue.js et GraphQL. Le projet comprendra les fonctionnalités suivantes :
- Afficher la liste des tâches
- Ajouter une nouvelle tâche
- Supprimer une tâche
Étape 1 : Création du projet Vue
Commencez par créer un nouveau projet Vue :
vue create task-manager
cd task-manager
Étape 2 : Installation des dépendances
Installez les packages nécessaires pour Apollo Client et les outils de développement :
npm install vue-apollo graphql apollo-client apollo-link-http apollo-cache-inmemory graphql-tag @vue/cli-service@latest --save
Étape 3 : Configuration d'Apollo Client
Créez un fichier apollo.js dans le répertoire src et ajoutez la configuration Apollo Client :
// src/apollo.js
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-client';
import { createApolloProvider } from '@vue/apollo-option';
const httpLink = new HttpLink({
uri: 'http://localhost:4000/graphql',
});
const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
});
export default apolloProvider;
Étape 4 : Configuration d'Apollo Provider
Importez et utilisez apolloProvider dans le fichier principal de l'application :
// src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import apolloProvider from './apollo';
Vue.config.productionTip = false;
new Vue({
provide: apolloProvider.provide(),
router,
store,
render: h => h(App),
}).$mount('#app');
Étape 5 : Création des composants
Créez un composant pour afficher la liste des tâches :
<!-- src/components/TasksList.vue -->
<template>
<div>
<h1>Tâches</h1>
<ul>
<li v-for="task in tasks" :key="task.id">
task.title - task.description
<button @click="deleteTask(task.id)">Supprimer</button>
</li>
</ul>
</div>
</template>
<script>
import { GET_TASKS_QUERY, DELETE_TASK_MUTATION } from '@/graphql';
export default {
data() {
return {
tasks: [],
};
},
apollo: {
tasks: {
query: GET_TASKS_QUERY,
fetchPolicy: 'network-only',
},
},
methods: {
async deleteTask(id) {
try {
await this.$apollo.mutate({
mutation: DELETE_TASK_MUTATION,
variables: { id },
});
} catch (error) {
console.error('Erreur lors de la suppression de la tâche', error);
}
},
},
};
</script>
Étape 6 : Création des mutations
Créez un fichier graphql.js pour stocker les queries et mutations :
// src/graphql.js
import gql from 'graphql-tag';
export const GET_TASKS_QUERY = gql`
query GetTasks {
tasks {
id
title
description
}
}
`;
export const CREATE_TASK_MUTATION = gql`
mutation CreateTask($title: String!, $description: String!) {
createTask(title: $title, description: $description) {
id
title
description
}
}
`;
export const DELETE_TASK_MUTATION = gql`
mutation DeleteTask($id: ID!) {
deleteTask(id: $id)
}
`;
Étape 7 : Ajout de la fonctionnalité d'ajout de tâche
Ajoutez un composant pour ajouter une nouvelle tâche :
<!-- src/components/AddTask.vue -->
<template>
<div>
<h2>Ajouter une tâche</h2>
<form @submit.prevent="addTask">
<input v-model="title" placeholder="Titre" required />
<textarea v-model="description" placeholder="Description"></textarea>
<button type="submit">Ajouter</button>
</form>
</div>
</template>
<script>
import { CREATE_TASK_MUTATION } from '@/graphql';
export default {
data() {
return {
title: '',
description: '',
};
},
methods: {
async addTask() {
try {
await this.$apollo.mutate({
mutation: CREATE_TASK_MUTATION,
variables: { title: this.title, description: this.description },
});
this.title = '';
this.description = '';
} catch (error) {
console.error('Erreur lors de l\'ajout de la tâche', error);
}
},
},
};
</script>
Étape 8 : Intégration des composants
Intégrez les composants dans le fichier App.vue :
<!-- src/App.vue -->
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
Étape 9 : Configuration des routes
Ajoutez les routes pour afficher la liste et ajouter une tâche :
// src/router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import TasksList from '@/components/TasksList.vue';
import AddTask from '@/components/AddTask.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'TasksList',
component: TasksList,
},
{
path: '/add-task',
name: 'AddTask',
component: AddTask,
},
],
});
Erreurs fréquentes et debugging
Erreur : Cannot read property 'map' of undefined
Cette erreur se produit lorsque la query GraphQL retourne une valeur undefined au lieu d'un objet.
## ❌ Mauvais
<template>
<div>
<h1>Tâches</h1>
<ul v-if="tasks">
<li v-for="task in tasks" :key="task.id">
task.title - task.description
</li>
</ul>
</div>
</template>
<script>
import { GET_TASKS_QUERY } from '@/graphql';
export default {
data() {
return {
tasks: [],
};
},
apollo: {
tasks: {
query: GET_TASKS_QUERY,
fetchPolicy: 'network-only',
},
},
};
</script>
vue
## ✅ Correct
<template>
<div>
<h1>Tâches</h1>
<ul v-if="tasks && tasks.length">
<li v-for="task in tasks" :key="task.id">
task.title - task.description
</li>
</ul>
<p v-else>Aucune tâche trouvée.</p>
</div>
</template>
<script>
import { GET_TASKS_QUERY } from '@/graphql';
export default {
data() {
return {
tasks: [],
};
},
apollo: {
tasks: {
query: GET_TASKS_QUERY,
fetchPolicy: 'network-only',
},
},
};
</script>
Erreur : Cannot read property 'id' of undefined
Cette erreur se produit lorsque la mutation GraphQL retourne une valeur undefined au lieu d'un objet.
## ❌ Mauvais
<template>
<div>
<h2>Ajouter une tâche</h2>
<form @submit.prevent="addTask">
<input v-model="title" placeholder="Titre" required />
<textarea v-model="description" placeholder="Description"></textarea>
<button type="submit">Ajouter</button>
</form>
</div>
</template>
<script>
import { CREATE_TASK_MUTATION } from '@/graphql';
export default {
data() {
return {
title: '',
description: '',
};
},
methods: {
async addTask() {
try {
await this.$apollo.mutate({
mutation: CREATE_TASK_MUTATION,
variables: { title: this.title, description: this.description },
});
this.title = '';
this.description = '';
} catch (error) {
console.error('Erreur lors de l\'ajout de la tâche', error);
}
},
},
};
</script>
vue
## ✅ Correct
<template>
<div>
<h2>Ajouter une tâche</h2>
<form @submit.prevent="addTask">
<input v-model="title" placeholder="Titre" required />
<textarea v-model="description" placeholder="Description"></textarea>
<button type="submit">Ajouter</button>
</form>
</div>
</template>
<script>
import { CREATE_TASK_MUTATION } from '@/graphql';
export default {
data() {
return {
title: '',
description: '',
};
},
methods: {
async addTask() {
try {
const result = await this.$apollo.mutate({
mutation: CREATE_TASK_MUTATION,
variables: { title: this.title, description: this.description },
});
if (result.data.createTask) {
this.title = '';
this.description = '';
}
} catch (error) {
console.error('Erreur lors de l\'ajout de la tâche', error);
}
},
},
};
</script>
Erreur : Cannot read property 'subscribe' of undefined
Cette erreur se produit lorsque la subscription GraphQL retourne une valeur undefined au lieu d'un objet.
## ❌ Mauvais
<template>
<div>
<h1>Nouvelles tâches</h1>
<ul>
<li v-for="task in newTasks" :key="task.id">
task.title - task.description
</li>
</ul>
</div>
</template>
<script>
import { NEW_TASK_SUBSCRIPTION } from '@/graphql';
export default {
data() {
return {
newTasks: [],
};
},
apollo: {
newTask: {
query: NEW_TASK_SUBSCRIPTION,
},
},
};
</script>
vue
## ✅ Correct
<template>
<div>
<h1>Nouvelles tâches</h1>
<ul>
<li v-for="task in newTasks" :key="task.id">
task.title - task.description
</li>
</ul>
</div>
</template>
<script>
import { NEW_TASK_SUBSCRIPTION } from '@/graphql';
export default {
data() {
return {
newTasks: [],
};
},
apollo: {
newTask: {
query: NEW_TASK_SUBSCRIPTION,
subscribeToResult: (result) => ({
data: result.data.newTask,
}),
},
},
};
</script>
Pour aller plus loin
1. Authentification avec JWT
Pour ajouter une authentification sécurisée à votre application, vous pouvez utiliser JSON Web Tokens (JWT). Cela impliquera la création d'un middleware pour vérifier les tokens et le renouvellement des sessions.
2. Optimisation des requêtes GraphQL
Utilisez les directives @include et @skip pour optimiser les requêtes GraphQL en récupérant uniquement les champs nécessaires.
query GetUser($id: ID!, $withEmail: Boolean) {
user(id: $id) @include(if: $withEmail) {
id
name
email
}
}
3. Tests unitaires avec Jest et Vue Test Utils
Écrivez des tests unitaires pour votre application en utilisant Jest et Vue Test Utils. Cela aidera à assurer la qualité du code et à prévenir les bugs futurs.
// src/components/TasksList.spec.js
import { shallowMount } from '@vue/test-utils';
import TasksList from '@/components/TasksList.vue';
describe('TasksList.vue', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(TasksList);
});
it('renders a list of tasks', async () => {
wrapper.vm.$apollo.queries.tasks.loading = false;
wrapper.vm.$apollo.queries.tasks.result = {
data: {
tasks: [
{ id: 1, title: 'Task 1', description: 'Description 1' },
{ id: 2, title: 'Task 2', description: 'Description 2' },
],
},
};
await wrapper.vm.$nextTick();
expect(wrapper.findAll('li')).toHaveLength(2);
});
});
Conclusion
En suivant ce tutoriel, vous avez appris à créer une application web complète utilisant Vue.js, Vuex, Vue Router et GraphQL. Vous avez également appris comment gérer les queries, mutations et subscriptions GraphQL, ainsi que comment optimiser les requêtes et ajouter des fonctionnalités de sécurité et de tests unitaires.
N'oubliez pas que ce tutoriel ne couvre qu'un aspect basique de la création d'une application avec Vue.js et GraphQL. Il existe de nombreux autres aspects à explorer pour améliorer votre application, comme l'utilisation du cache Apollo, la gestion des erreurs et le développement côté serveur avec Node.js.