Presque toutes les applications web modernes consomment des APIs REST. Fetch, Axios, React Query — il y a plusieurs façons de le faire, et toutes ont leurs cas d’usage. Ce guide couvre les patterns essentiels : faire des requêtes, gérer les erreurs, authentifier, et structurer son code d’API de façon maintenable.
Fetch API — les bases
// GET — récupérer des données
const response = await fetch('/api/users')
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const users = await response.json()
// POST — envoyer des données
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' }),
})
const newUser = await response.json()
// PUT/PATCH — mettre à jour
const response = await fetch(`/api/users/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice Updated' }),
})
// DELETE
await fetch(`/api/users/${id}`, { method: 'DELETE' })
Créer une couche d’abstraction API
Ne pas appeler fetch directement partout dans votre code. Créez une fonction centrale qui gère les headers communs, les erreurs, et l’authentification :
// lib/api.ts
const BASE_URL = process.env.NEXT_PUBLIC_API_URL || '/api'
class ApiError extends Error {
constructor(public status: number, message: string) {
super(message)
this.name = 'ApiError'
}
}
async function apiFetch(endpoint: string, options?: RequestInit): Promise {
const token = localStorage.getItem('auth_token')
const response = await fetch(`${BASE_URL}${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...options?.headers,
},
})
if (!response.ok) {
const error = await response.json().catch(() => ({ message: 'Erreur serveur' }))
throw new ApiError(response.status, error.message)
}
return response.json()
}
// API typée
export const api = {
get: (url: string) => apiFetch(url),
post: (url: string, data: unknown) =>
apiFetch(url, { method: 'POST', body: JSON.stringify(data) }),
patch: (url: string, data: unknown) =>
apiFetch(url, { method: 'PATCH', body: JSON.stringify(data) }),
delete: (url: string) => apiFetch(url, { method: 'DELETE' }),
}
// Usage
const users = await api.get('/users')
const newUser = await api.post('/users', { name: 'Bob' })
Axios — quand l’utiliser
Axios reste pertinent pour ses fonctionnalités supplémentaires : intercepteurs, timeout configurable, upload de fichiers avec progress, annulation de requêtes simplifiée.
import axios from 'axios'
// Instance avec config partagée
const apiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
timeout: 10000,
})
// Intercepteur — ajouter le token à toutes les requêtes
apiClient.interceptors.request.use(config => {
const token = localStorage.getItem('auth_token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
// Intercepteur — gérer les 401 globalement
apiClient.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// Rediriger vers login, refresh token, etc.
redirectToLogin()
}
return Promise.reject(error)
}
)
// Axios parse automatiquement le JSON et throw sur les erreurs HTTP
const { data: users } = await apiClient.get('/users')
React Query — la solution complète
Pour les applications React, React Query (TanStack Query) gère bien plus que le simple fetch : cache, synchronisation en arrière-plan, loading/error state, pagination, mutations.
// Installation
npm install @tanstack/react-query
// Setup dans _app.tsx ou layout.tsx
const queryClient = new QueryClient()
// Fetcher des données
function UserList() {
const { data: users, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => api.get('/users'),
staleTime: 5 * 60 * 1000, // données fraîches pendant 5 minutes
})
if (isLoading) return
if (error) return
return {users.map(u => - {u.name}
)}
}
// Mutations
function CreateUserForm() {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: (data: CreateUserDto) => api.post('/users', data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] }) // re-fetch la liste
},
})
return (
)
}
Gestion des erreurs — les patterns
// Pattern 1 : Error Boundary pour les erreurs de rendu
// Pattern 2 : état local d'erreur pour les requêtes
// Distinguer les types d'erreurs
try {
const user = await api.get(`/users/${id}`)
} catch (error) {
if (error instanceof ApiError) {
if (error.status === 404) showNotFound()
else if (error.status === 403) showForbidden()
else showGenericError(error.message)
} else {
// Erreur réseau — pas de connexion
showNetworkError()
}
}
Offres développeur JavaScript et React
Des postes pour développeurs qui savent intégrer des APIs et structurer des couches de données propres.
À lire aussi : JavaScript asynchrone — Hooks React — TypeScript