Étape 10 — Migrer fetch vers Axios + loading et erreurs
Objectifs
- Remplacer
fetch()par Axios dans le store Pinia - Afficher un squelette de chargement (
v-skeleton-loader) pendant les requêtes - Afficher une alerte (
v-alert) si l'API est indisponible - Comprendre les 3 états d'un appel API : loading, error, data
Résultat attendu

Contexte
Le store pokemonStore utilise encore fetch() pour charger les Pokémon. On va le migrer vers Axios pour profiter de la configuration centralisée (base URL, headers, gestion d'erreurs).
En parallèle, on va rendre l'interface robuste : au lieu d'un écran blanc quand l'API ne répond pas, l'utilisateur verra un message d'erreur clair.
Encart oral — Les 3 états d'un appel API
Tout appel réseau a exactement 3 états possibles :
- Loading — la requête est en cours (afficher un indicateur)
- Error — la requête a échoué (afficher un message d'erreur)
- Data — la requête a réussi (afficher les données)
Un bon développeur gère toujours ces 3 états. Ne jamais laisser l'utilisateur devant un écran vide sans explication.
Composants Vuetify utilisés
| Composant | Rôle | Documentation |
|---|---|---|
<v-skeleton-loader> | Affiche un placeholder animé pendant le chargement | Skeleton Loaders |
<v-alert> | Affiche un message d'information, d'erreur ou d'avertissement | Alerts |
Tâches
1. Migrer le store vers Axios
Dans src/stores/pokemonStore.js, remplacez fetch() par Axios.
Avant (fetch) :
async fetchPokemons ({ withLoader = true } = {}) {
if (withLoader) this.isLoading = true
try {
const response = await fetch('http://localhost:3535/pokemons')
this.pokemons = await response.json()
} catch (error) {
console.error('Erreur:', error.message)
this.pokemons = []
} finally {
if (withLoader) this.isLoading = false
}
}Après (Axios) :
import api from '@/plugins/axios'
// ... dans les actions :
async fetchPokemons ({ withLoader = true } = {}) {
if (withLoader) this.isLoading = true
try {
const response = await api.get('/pokemons')
this.pokemons = response.data
} catch (error) {
console.error('Erreur:', error.message)
this.pokemons = []
} finally {
if (withLoader) this.isLoading = false
}
}Faites la même migration pour fetchTypes :
async fetchTypes ({ withLoader = true } = {}) {
if (withLoader) this.isLoading = true
try {
const response = await api.get('/types')
this.types = response.data
} catch (error) {
console.error('Erreur:', error.message)
this.types = []
} finally {
if (withLoader) this.isLoading = false
}
}Notez les différences :
- Import :
import api from '@/plugins/axios'au lieu de rien (fetch est global) - Appel :
api.get('/pokemons')au lieu defetch('http://localhost:3535/pokemons') - Données :
response.dataau lieu deawait response.json()(Axios parse automatiquement le JSON) - URL :
/pokemonsau lieu de l'URL complète (grâce à labaseURL)
2. Ajouter le skeleton loader dans index.vue
Le state isLoading du store est déjà mis à true pendant les requêtes. On va l'utiliser pour afficher des squelettes de chargement.
Dans src/pages/index.vue, ajoutez avant la grille de cartes :
<!-- Squelettes de chargement pendant la requête API -->
<v-row v-if="pokemonStore.isLoading">
<v-col
v-for="n in 8"
:key="n"
cols="12"
sm="6"
md="4"
lg="3"
>
<v-skeleton-loader
type="image, article"
height="350"
/>
</v-col>
</v-row>Le squelette simule la forme des cartes Pokémon. Le type="image, article" dessine un rectangle (image) suivi de lignes de texte (titre + sous-titre).
3. Ajouter l'alerte d'erreur
Toujours dans index.vue, ajoutez une alerte qui s'affiche quand la liste est vide et que le chargement est terminé :
<!-- Message d'erreur si aucun Pokémon chargé -->
<v-alert
v-else-if="pokemonStore.pokemons.length === 0"
type="error"
variant="tonal"
class="mb-6"
>
Impossible de charger les Pokémon. Vérifiez que l'API tourne sur
{{ apiUrl }}.
</v-alert>
<!-- Grille de cartes (cas normal) -->
<v-row v-else>
<!-- ... vos v-col + PokemonCard ... -->
</v-row>Et dans le <script setup>, ajoutez la variable pour l'URL :
// URL de l'API pour le message d'erreur
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3535'Les trois blocs (v-if, v-else-if, v-else) couvrent les 3 états :
v-if="pokemonStore.isLoading" → Loading (skeleton)
v-else-if="pokemons.length === 0" → Error (alert)
v-else → Data (grille)4. Tester les 3 états
- État data : avec l'API qui tourne (
npm run apidans le dépôt API), rechargez la page → les Pokémon s'affichent - État loading : ouvrez DevTools → onglet Network → activez le throttle "Slow 3G" → rechargez la page → les squelettes s'affichent
- État error : mettez une URL invalide dans
.env(ex :VITE_API_URL=http://localhost:9999), relancez → l'alerte d'erreur s'affiche
Tests
- Le store utilise
api.get()au lieu defetch() - Les squelettes s'affichent pendant le chargement
- L'alerte d'erreur s'affiche quand l'API est indisponible
- 0 erreurs dans la console
Solutions
src/stores/pokemonStore.js (actions modifiées)
import { defineStore } from 'pinia'
import api from '@/plugins/axios'
export const usePokemonStore = defineStore('pokemon', {
state: () => ({
isLoading: false,
types: [],
pokemons: [],
}),
getters: {
totalPokemons: state => state.pokemons.length,
getTypeById: state => typeId => {
return state.types.find(type => type.id === typeId)
},
getPokemonById: state => pokemonId => {
return state.pokemons.find(pokemon => pokemon.id === pokemonId)
},
},
actions: {
async init () {
console.log('Initialisation du store Pokémon...')
this.isLoading = true
try {
await Promise.all([
this.fetchTypes({ withLoader: false }),
this.fetchPokemons({ withLoader: false }),
])
console.log('Store Pokémon initialisé')
} catch (error) {
console.error('Erreur lors de l\'initialisation:', error)
} finally {
this.isLoading = false
}
},
async fetchTypes ({ withLoader = true } = {}) {
if (withLoader) this.isLoading = true
try {
const response = await api.get('/types')
this.types = response.data
} catch (error) {
console.error('Erreur lors du chargement des types:', error.message)
this.types = []
} finally {
if (withLoader) this.isLoading = false
}
},
async fetchPokemons ({ withLoader = true } = {}) {
if (withLoader) this.isLoading = true
try {
const response = await api.get('/pokemons')
this.pokemons = response.data
} catch (error) {
console.error('Erreur lors du chargement des Pokémon:', error.message)
this.pokemons = []
} finally {
if (withLoader) this.isLoading = false
}
},
},
})src/pages/index.vue (version avec les 3 états)
<template>
<v-container>
<h1 class="text-h3 text-center my-6">
Pokédex
<span class="text-subtitle-1">({{ pokemonStore.totalPokemons }})</span>
</h1>
<!-- État 1 : Loading — squelettes de chargement -->
<v-row v-if="pokemonStore.isLoading">
<v-col
v-for="n in 8"
:key="n"
cols="12"
sm="6"
md="4"
lg="3"
>
<v-skeleton-loader
type="image, article"
height="350"
/>
</v-col>
</v-row>
<!-- État 2 : Error — alerte si aucun Pokémon chargé -->
<v-alert
v-else-if="pokemonStore.pokemons.length === 0"
type="error"
variant="tonal"
class="mb-6"
>
Impossible de charger les Pokémon. Vérifiez que l'API tourne sur
{{ apiUrl }}.
</v-alert>
<!-- État 3 : Data — grille de cartes Pokémon -->
<v-row v-else>
<v-col
v-for="pokemon in pokemonStore.pokemons"
:key="pokemon.id"
cols="12"
sm="6"
md="4"
lg="3"
>
<pokemon-card :pokemon="pokemon" />
</v-col>
</v-row>
</v-container>
</template>
<script setup>
import { usePokemonStore } from '@/stores/pokemonStore'
import PokemonCard from '@/components/PokemonCard.vue'
const pokemonStore = usePokemonStore()
// URL de l'API pour le message d'erreur
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3535'
</script>Commit
git add -A
git commit -m "feat: migration Axios, skeleton loader et gestion erreurs"