Skip to content

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 :

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 URLNon (URL complète à chaque appel)Oui (baseURL configuré une fois)
Parsing JSONManuel (response.json())Automatique (response.data)
Headers par défautNonOui (defaults.headers)
IntercepteursNonOui (requête et réponse)
Gestion d'erreurUniquement erreurs réseauErreurs réseau + codes HTTP 4xx/5xx
TimeoutNon natifOui (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

bash
npm install axios

Vé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é :

js
// 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 api

Pourquoi 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:3535

Fichier .env.example (commité dans le dépôt)

VITE_API_URL=http://localhost:3535

Règles importantes

FichierCommité ?Contenu
.envNon (dans .gitignore)Vraies valeurs, potentiellement secrètes
.env.exampleOuiModè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 :

js
// 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 :

  1. Le fichier .env existe bien à la racine du projet (à côté de package.json)
  2. La variable commence bien par VITE_
  3. Vous avez relancé npm run dev aprè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

js
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

js
const response = await api.get(`/pokemons/${id}`)
const pokemon = response.data

POST — Envoyer des données

js
const response = await api.post('/pokemons', {
  name: 'Pikachu',
  type: 'electric',
})
const newPokemon = response.data

DELETE — Supprimer un élément

js
await api.delete(`/pokemons/${id}`)

Migration de fetch() vers Axios

Avant (fetch)

js
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)

js
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 : /pokemons suffit (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

js
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 :

js
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 :

ÉtatVariableComposant Vuetify
ChargementisLoadingv-skeleton-loader ou v-progress-circular
Erreurerrorv-alert type="error"
DonnéespokemonsContenu normal (cartes, liste, etc.)
vue
<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 :

  1. Créez public/mock.json avec un échantillon de données
  2. Dans le store, ajoutez un fallback :
js
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

ErreurCauseSolution
Network ErrorAPI éteinte ou URL incorrecteVérifier que l'API tourne, vérifier .env
VITE_API_URL is undefinedVariable non préfixée ou serveur pas relancéAjouter VITE_, relancer npm run dev
response.data is undefinedConfusion fetch/AxiosAvec Axios c'est response.data, pas response.json()
CORS errorL'API refuse les requêtes du navigateurConfigurer 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é

  1. Le composant utilise le store Pinia pour accéder aux données
  2. Le store appelle le plugin Axios pour faire les requêtes HTTP
  3. Le plugin Axios lit l'URL de base depuis le fichier .env
  4. Axios envoie la requête à l'API et retourne les données au store

Documentation pour les cours de développement web