Axios — Client HTTP pour Vue.js
Axios est une bibliothèque JavaScript qui simplifie les appels HTTP. Dans un projet Vue.js, il remplace avantageusement fetch() natif grâce à sa configuration centralisée, ses intercepteurs et sa gestion d'erreurs automatique.
Page de référence vs exercices
Cette page présente Axios avec les meilleures pratiques (axios.create(), exemples épurés). C'est une référence pour comprendre les concepts et pour votre projet personnel.
Pour l'implémentation pas-à-pas dans le Pokédex, suivez les exercices :
- E9 — Configurer Axios (installation,
.env, plugin) - E10 — Loading, erreurs, mock (migration fetch → Axios, skeleton loader)
Les exercices et cette page utilisent la même approche (axios.create()). Le code est identique et interchangeable.
Pourquoi Axios plutôt que fetch() ?
fetch() | Axios | |
|---|---|---|
| Base URL | Non (URL complète à chaque appel) | Oui (baseURL configuré une fois) |
| Parsing JSON | Manuel (response.json()) | Automatique (response.data) |
| Headers par défaut | Non | Oui (defaults.headers) |
| Intercepteurs | Non | Oui (requête et réponse) |
| Gestion d'erreur | Uniquement erreurs réseau | Erreurs réseau + codes HTTP 4xx/5xx |
| Timeout | Non natif | Oui (timeout en ms) |
Quand utiliser fetch() vs Axios ?
fetch() est parfait pour apprendre les bases et pour des cas simples (un seul appel, pas de configuration). Dès qu'un projet a plusieurs appels API, une URL de base commune ou des headers récurrents, Axios devient le meilleur choix.
Installation
npm install axiosVérifiez que axios apparaît bien dans les dependencies de votre package.json.
Configuration avec axios.create()
La bonne pratique est de créer une instance Axios configurée dans un fichier dédié :
// src/plugins/axios.js
import axios from 'axios'
// import.meta.env.VITE_API_URL → lit la variable VITE_API_URL depuis le fichier .env
// || 'http://localhost:3535' → valeur par défaut si la variable n'existe pas
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3535'
// axios.create() crée une instance Axios avec une configuration réutilisable
// baseURL → toutes les requêtes partiront de cette URL (ex : api.get('/pokemons')
// enverra une requête à http://localhost:3535/pokemons)
// headers → en-têtes HTTP envoyés automatiquement avec chaque requête
const api = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json', // On envoie du JSON
'Accept-Language': 'fr', // On demande les réponses en français
},
})
// On exporte l'instance pour l'utiliser partout : import api from '@/plugins/axios'
export default apiPourquoi axios.create() ?
Créer une instance avec axios.create() plutôt que modifier axios.defaults permet d'avoir plusieurs configurations dans un même projet (ex : une pour votre API, une pour un service tiers). C'est la pratique recommandée.
Variables d'environnement
L'URL de l'API ne doit jamais être écrite en dur dans le code. On utilise un fichier .env :
Fichier .env (à la racine du projet)
VITE_API_URL=http://localhost:3535Fichier .env.example (commité dans le dépôt)
VITE_API_URL=http://localhost:3535Règles importantes
| Fichier | Commité ? | Contenu |
|---|---|---|
.env | Non (dans .gitignore) | Vraies valeurs, potentiellement secrètes |
.env.example | Oui | Modèle pour les autres développeurs |
Préfixe VITE_ obligatoire
Avec Vite, seules les variables préfixées par VITE_ sont accessibles dans le code côté client via import.meta.env.VITE_*. Une variable sans ce préfixe sera invisible.
Redémarrage obligatoire
Les fichiers .env sont lus au démarrage du serveur Vite. Après toute modification de .env, relancez npm run dev.
Vérifier que .env fonctionne
Avant d'aller plus loin, assurez-vous que la variable d'environnement est bien accessible. Dans src/plugins/axios.js, ajoutez temporairement :
// import.meta.env.VITE_API_URL → lit la variable VITE_API_URL depuis le fichier .env
// || 'http://localhost:3535' → valeur par défaut si la variable n'existe pas
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3535'
// Vérification temporaire — à supprimer après le test
console.log('API URL :', API_BASE_URL)Ouvrez la console du navigateur (F12) et vérifiez que l'URL s'affiche correctement. Si vous voyez http://localhost:3535, c'est bon. Si vous voyez undefined, vérifiez :
- Le fichier
.envexiste bien à la racine du projet (à côté depackage.json) - La variable commence bien par
VITE_ - Vous avez relancé
npm run devaprès avoir créé/modifié le.env
Pensez à supprimer le console.log une fois le test validé. Aucun console.log de debug ne doit rester dans le code final.
Utilisation de base
GET — Récupérer des données
import api from '@/plugins/axios'
// Avec Axios (simple et propre)
const response = await api.get('/pokemons')
const pokemons = response.data
// Équivalent avec fetch (plus verbeux)
const response = await fetch('http://localhost:3535/pokemons')
const pokemons = await response.json() GET — Récupérer un élément par ID
const response = await api.get(`/pokemons/${id}`)
const pokemon = response.dataPOST — Envoyer des données
const response = await api.post('/pokemons', {
name: 'Pikachu',
type: 'electric',
})
const newPokemon = response.dataDELETE — Supprimer un élément
await api.delete(`/pokemons/${id}`)Migration de fetch() vers Axios
Avant (fetch)
async fetchPokemons() {
const response = await fetch('http://localhost:3535/pokemons')
if (!response.ok) {
throw new Error(`Erreur HTTP : ${response.status}`)
}
this.pokemons = await response.json()
}Après (Axios)
import api from '@/plugins/axios'
async fetchPokemons() {
const response = await api.get('/pokemons')
this.pokemons = response.data
}Points clés de la migration :
- Plus de base URL :
/pokemonssuffit (la base URL est dans.env) - Plus de
.json(): Axios parse automatiquement le JSON - Plus de
response.ok: Axios lance une erreur automatiquement pour les codes 4xx/5xx
Utilisation dans un store Pinia
import { defineStore } from 'pinia'
import api from '@/plugins/axios'
export const usePokemonStore = defineStore('pokemon', {
state: () => ({
pokemons: [],
isLoading: false,
error: null,
}),
getters: {
totalPokemons: (state) => state.pokemons.length,
},
actions: {
async fetchPokemons() {
const response = await api.get('/pokemons')
this.pokemons = response.data
},
async init() {
this.isLoading = true
this.error = null
try {
await this.fetchPokemons()
} catch (error) {
this.error = error.message
console.error('Erreur lors du chargement :', error)
} finally {
this.isLoading = false
}
},
},
})Qui gère isLoading ?
fetchPokemons() ne touche pas à isLoading. C'est init() qui orchestre le chargement : elle met isLoading à true au début, lance les requêtes en parallèle, et le remet à false dans finally quand tout est terminé.
Si chaque action gérait isLoading individuellement, il passerait à false dès que la première requête termine — alors que la deuxième serait encore en cours.
Règle : isLoading = true se met dans la fonction qui orchestre le chargement, pas dans chaque action individuelle.
Plus tard (E10 — Loading et erreurs), les actions individuelles pourront aussi gérer le loading via un paramètre optionnel withLoader :
async fetchPokemons(withLoader = false) {
if (withLoader) this.isLoading = true
try {
const response = await api.get('/pokemons')
this.pokemons = response.data
} finally {
if (withLoader) this.isLoading = false
}
}Cela permet d'afficher le skeleton quand on recharge une seule ressource (après un ajout/suppression), sans le déclencher quand c'est init() qui appelle.
Les 3 états d'un appel API
Tout appel API passe par 3 états qu'il faut gérer dans l'interface :
| État | Variable | Composant Vuetify |
|---|---|---|
| Chargement | isLoading | v-skeleton-loader ou v-progress-circular |
| Erreur | error | v-alert type="error" |
| Données | pokemons | Contenu normal (cartes, liste, etc.) |
<template>
<!-- État 1 : Chargement -->
<v-skeleton-loader v-if="isLoading" type="card" />
<!-- État 2 : Erreur -->
<v-alert v-else-if="error" type="error">
{{ error }}
</v-alert>
<!-- État 3 : Données -->
<v-row v-else>
<v-col v-for="pokemon in pokemons" :key="pokemon.id">
<PokemonCard :pokemon="pokemon" />
</v-col>
</v-row>
</template>Mock JSON — Filet de sécurité
Un fichier mock permet à l'application de fonctionner même si l'API est indisponible :
- Créez
public/mock.jsonavec un échantillon de données - Dans le store, ajoutez un fallback :
async fetchPokemons() {
try {
const response = await api.get('/pokemons')
this.pokemons = response.data
} catch {
// Fallback : charger les données mockées
const response = await fetch('/mock.json')
this.pokemons = await response.json()
}
}Quand utiliser le mock ?
Le mock est utile pendant le développement (API locale éteinte), en démo (pas de connexion), ou pour les tests automatisés.
Erreurs fréquentes
| Erreur | Cause | Solution |
|---|---|---|
Network Error | API éteinte ou URL incorrecte | Vérifier que l'API tourne, vérifier .env |
VITE_API_URL is undefined | Variable non préfixée ou serveur pas relancé | Ajouter VITE_, relancer npm run dev |
response.data is undefined | Confusion fetch/Axios | Avec Axios c'est response.data, pas response.json() |
CORS error | L'API refuse les requêtes du navigateur | Configurer CORS côté serveur |
Qu'est-ce que CORS ?
CORS (Cross-Origin Resource Sharing) est une sécurité du navigateur qui bloque les requêtes vers un domaine différent de celui de votre application.
Exemple : votre app tourne sur http://localhost:3000 et appelle une API sur http://localhost:3535. Ce sont deux origines différentes (ports différents). Le navigateur bloque la requête par défaut.
Pourquoi ? Sans cette protection, n'importe quel site web pourrait envoyer des requêtes à votre banque en ligne avec vos cookies de session.
Solution : c'est l'API (le serveur) qui doit autoriser les requêtes en ajoutant un header Access-Control-Allow-Origin. En tant que développeur frontend, vous ne pouvez pas contourner CORS — c'est au développeur backend de le configurer.
Dans notre cas, l'API Pokédex locale est déjà configurée pour accepter les requêtes CORS. Si vous utilisez une API publique qui bloque CORS, vérifiez sa documentation.
Résumé
- Le composant utilise le store Pinia pour accéder aux données
- Le store appelle le plugin Axios pour faire les requêtes HTTP
- Le plugin Axios lit l'URL de base depuis le fichier
.env - Axios envoie la requête à l'API et retourne les données au store