Maîtriser les appels HTTP en React

fetch, Axios et API Client typé

Utilisez les flèches, cliquez ou glissez pour naviguer

Objectifs de la leçon

1. Comprendre fetch()

Et ses pièges (pas de throw sur erreurs HTTP)

2. Utiliser Axios

Appels HTTP plus ergonomiques

3. Construire un client API

Réutilisable avec interceptors

4. Typer les réponses

Interfaces TypeScript

5. Configurer les variables d'environnement

Et comprendre CORS

Plan du cours

1

Rappel HTTP

Verbes, status codes, headers, JSON

2

fetch() natif

Syntaxe, async/await, gestion d'erreur

3

Axios

Installation, avantages, interceptors

4

Client API réutilisable

baseURL, headers, TypeScript

5

Live coding

Connecter React à une API REST

Rappel HTTP

Ce que le backend attend de vous

HTTP = HyperText Transfer Protocol

Le langage de communication client-serveur

Les verbes HTTP

GET

Récupérer des données

Sans effet de bord

POST

Créer une ressource

Envoyer des données

PUT

Remplacer une ressource

Mise à jour complète

PATCH

Modifier partiellement

Mise à jour partielle

DELETE

Supprimer une ressource

Les codes de statut HTTP

2xx

Succès

200 OK, 201 Created, 204 No Content

3xx

Redirection

301 Moved, 304 Not Modified

4xx

Erreur client

400 Bad Request, 401 Unauthorized, 404 Not Found

5xx

Erreur serveur

500 Internal Server Error, 503 Service Unavailable

Headers & JSON

Request Headers

Content-Type: application/json

Authorization: Bearer <token>

Accept: application/json

Pourquoi JSON ?

  • • Format standard des APIs REST
  • • Lisible par humains et machines
  • • Compatible avec JavaScript natif
  • • Léger et flexible

💡 Toujours définir Content-Type: application/json quand vous envoyez du JSON

fetch() natif

L'API native du navigateur

Disponible dans tous les navigateurs modernes

Pas d'installation nécessaire

Syntaxe de base avec fetch

const response = await fetch('https://api.example.com/users')

const data = await response.json()

// data contient les utilisateurs

1. fetch()

Retourne une Promise<Response>

2. response.json()

Parse le body en JSON (async!)

Exemple complet avec async/await

async function getUsers() {

try {

const response = await fetch('https://api.example.com/users')

const data = await response.json()

return data

} catch (error) {

console.error('Erreur:', error)

}

}

Deux await nécessaires : un pour la réponse, un pour le JSON

⚠️ LE PIÈGE CRITIQUE

fetch() ne throw PAS sur les erreurs HTTP!

404 Not Found → Pas d'exception!

500 Server Error → Pas d'exception!

401 Unauthorized → Pas d'exception!

Le catch() n'attrape QUE les erreurs réseau

Démonstration du piège

❌ Code trompeur

try {

const res = await fetch('/api/404')

const data = await res.json()

// data = { error: 'Not found' }

// Pas d'erreur lancée!

} catch (e) {

// Jamais exécuté!

}

✅ Code correct

try {

const res = await fetch('/api/users')

if (!res.ok) {

throw new Error(`HTTP ${res.status}`)

}

const data = await res.json()

} catch (e) {

// Gère les erreurs HTTP!

}

🔑 response.ok est un booléen (true si status 200-299)

POST avec fetch()

const response = await fetch('/api/users', {

method: 'POST',

headers: {

'Content-Type': 'application/json',

},

body: JSON.stringify({

name: 'Alice',

email: '[email protected]'

})

})

method

GET, POST, PUT, DELETE

headers

Objet key-value

body

String (JSON.stringify)

Axios

Le client HTTP populaire

Plus ergonomique que fetch

Gère automatiquement les erreurs HTTP

Installation d'Axios

# npm

npm install axios

# yarn

yarn add axios

# pnpm

pnpm add axios

📦 ~13 KB gzipped — worth it!

Axios vs fetch — Comparaison

Critère fetch() Axios
Installation Natif npm install
JSON parsing Manuel (await res.json()) Automatique
Erreurs HTTP (4xx/5xx) Ne throw pas Throw automatique
Timeout AbortController complexe Simple config
Interceptors Non Oui
Taille 0 KB ~13 KB

Axios : plus intuitif pour la gestion d'erreurs

Syntaxe Axios

GET

import axios from 'axios'

const response = await axios.get('/api/users')

const users = response.data

POST

const response = await axios.post('/api/users', {

name: 'Alice',

email: '[email protected]'

})

✅ Pas besoin de JSON.stringify — Axios le fait automatiquement!

Axios throw automatiquement!

try {

const response = await axios.get('/api/users/999')

// Si 404, on n'arrive jamais ici!

} catch (error) {

if (error.response) {

// Erreur HTTP (4xx ou 5xx)

console.log(error.response.status) // 404

} else {

// Erreur réseau

}

}

error.response contient la réponse HTTP même en cas d'erreur

Construire un Client API

Réutilisable et typé

Une instance Axios configurée une seule fois

Utilisée partout dans l'application

Créer une instance Axios

import axios from 'axios'

const api = axios.create({

baseURL: 'https://api.example.com',

timeout: 5000,

headers: {

'Content-Type': 'application/json',

},

})

export default api

baseURL

Préfixe pour toutes les requêtes

timeout

5 secondes max

headers

Défaut pour toutes les requêtes

Request Interceptor — Auth automatique

api.interceptors.request.use((config) => {

const token = localStorage.getItem('token')

if (token) {

config.headers.Authorization = `Bearer ${token}`

}

return config

})

Chaque requête inclut automatiquement le token JWT

Plus besoin de le gérer manuellement dans chaque appel

Response Interceptor — Gestion globale

api.interceptors.response.use(

// Succès

(response) => response,

// Erreur

(error) => {

if (error.response?.status === 401) {

// Token expiré → redirect login

window.location.href = '/login'

}

return Promise.reject(error)

}

)

💡 Gestion centralisée des erreurs 401 — redirection automatique vers login

Typer les réponses avec TypeScript

interface User {

id: number

name: string

email: string

phone?: string // optionnel

}

interface ApiResponse<T> {

data: T

status: number

}

Utiliser api.get<User[]>('/users') pour typer la réponse

Variables d'environnement

Configurer sans hardcoded

Différentes URLs pour dev, staging, production

Jamais de secrets dans le code!

Configuration .env avec Vite

.env.local

VITE_API_URL=http://localhost:3000/api

VITE_API_KEY=dev-key-123

api.ts

const api = axios.create({

baseURL: import.meta.env.VITE_API_URL,

})

⚠️ Seules les variables préfixées par VITE_ sont exposées au client

CORS

Cross-Origin Resource Sharing

Pourquoi vos requêtes échouent parfois

Et comment les résoudre

Qu'est-ce que CORS ?

Le problème

Frontend sur localhost:5173 appelle API sur localhost:3000

Origines différentes = blocage par le navigateur

La solution

Le serveur doit autoriser votre origine

Header: Access-Control-Allow-Origin: *

⚠️ CORS se configure côté SERVEUR, pas côté frontend!

C'est une protection du navigateur pour l'utilisateur

Live Coding

Connecter React à une API REST

API publique : JSONPlaceholder

https://jsonplaceholder.typicode.com

Structure du projet

src/

api/

client.ts

types.ts

users.ts

hooks/

useUsers.ts

components/

UserList.tsx

api/

Client Axios + types

hooks/

Logique de fetch

components/

UI

api/types.ts

export interface User {

id: number

name: string

username: string

email: string

phone: string

website: string

company: {

name: string

}

}

Interface basée sur la réponse de JSONPlaceholder

api/client.ts

import axios from 'axios'

const api = axios.create({

baseURL: import.meta.env.VITE_API_URL,

timeout: 10000,

headers: { 'Content-Type': 'application/json' },

})

export default api

✅ Une seule instance configurée — réutilisée partout

api/users.ts

import api from './client'

import type { User } from './types'

export const userApi = {

getAll: async (): Promise<User[]> => {

const { data } = await api.get<User[]>('/users')

return data

},

getById: async (id: number): Promise<User> => {

const { data } = await api.get<User>(`/users/${id}`)

return data

},

}

Fonctions typées — IntelliSense complet dans VS Code

hooks/useUsers.ts

import { useState, useEffect } from 'react'

import { userApi } from '../api/users'

import type { User } from '../api/types'

export function useUsers() {

const [users, setUsers] = useState<User[]>([])

const [loading, setLoading] = useState(true)

const [error, setError] = useState<string | null>(null)

Trois états : data, loading, error — le pattern standard

hooks/useUsers.ts (suite)

useEffect(() => {

async function fetchUsers() {

try {

const data = await userApi.getAll()

setUsers(data)

} catch (err) {

setError('Erreur lors du chargement')

} finally {

setLoading(false)

}

}

fetchUsers()

}, [])

return { users, loading, error }

}

components/UserList.tsx

import { useUsers } from '../hooks/useUsers'

export function UserList() {

const { users, loading, error } = useUsers()

if (loading) return <p>Chargement...</p>

if (error) return <p>{error}</p>

return (

<ul>

{users.map((user) => (

<li key={user.id}>{user.name}</li>

))}

</ul>

)

}

⚠️ Pièges courants

Les erreurs à éviter absolument

Piège #1 : Oublier await sur response.json()

❌ Incorrect

const data = response.json()

// data est une Promise!

✅ Correct

const data = await response.json()

// data est l'objet JSON

💡 Double await nécessaire avec fetch : un pour la réponse, un pour le JSON

Piège #2 : Croire que catch attrape les 404

Avec fetch(), le catch() n'attrape PAS les erreurs HTTP!

try {

const res = await fetch('/api/inexistant')

// res.status = 404, mais pas d'erreur!

} catch (e) {

// Jamais exécuté pour un 404

}

✅ Solution : Vérifier response.ok ou utiliser Axios

Piège #3 : Stocker le token API en dur

❌ Jamais!

const API_KEY = 'sk-abc123...'

// Visible dans le code client!

✅ Utiliser .env

const key = import.meta.env.VITE_API_KEY

// Configurable par environnement

⚠️ Les clés API exposées au client sont visibles par tous — utiliser un backend comme proxy si possible

Piège #4 : Confondre CORS frontend/backend

CORS se configure côté SERVEUR, pas côté frontend!

❌ Frontend ne peut rien faire

Pas de config magique dans React

✅ Backend doit autoriser

Header Access-Control-Allow-Origin

💡 En développement, utiliser un proxy Vite ou configurer le backend

Points clés à retenir

fetch()

  • • Ne throw PAS sur 4xx/5xx
  • • Vérifier response.ok
  • • Double await pour JSON

Axios

  • • Throw automatique sur erreurs
  • • Auto-JSON parsing
  • • Interceptors puissants

API Client

  • • Une instance axios.create()
  • • TypeScript pour les types
  • • Interceptors pour auth

Sécurité

  • • Variables d'env (.env)
  • • CORS = config serveur
  • • Jamais de clés en dur

Récapitulatif

Vous avez appris

  • • fetch() et ses subtilités
  • • Axios pour plus de confort
  • • Créer un client API typé

Prochaines étapes

  • • React Query pour le caching
  • • SWR pour le data fetching
  • • Authentification JWT

Questions ?