# Chapitre 1 : useApi Le hook `useApi` simplifie les appels API avec gestion automatique de l'authentification JWT, du rafraîchissement de token et de la gestion des erreurs. ## Configuration L'API est configurée dans `appConfig.js` : ```javascript // src/appConfig.js export const config = { api: { prefixUrl: import.meta.env.VITE_API_URL, timeout: 30000, debug: import.meta.env.DEV, paths: { login: "login", logout: "logout", refresh: "refresh" } } }; ``` ## Structure retournée ```javascript import { useApi } from '@cap-rel/smartcommon'; const api = useApi(); ``` ^ Méthode ^ Description ^ | api.user | Objet utilisateur connecté | | api.login(body) | Connexion utilisateur | | api.logout() | Déconnexion | | api.entities() | Récupérer les entités disponibles | | api.device(body) | Enregistrer un appareil | | api.public | Instance ky pour requêtes publiques | | api.private | Instance ky pour requêtes authentifiées | | api.get(url, options) | Raccourci GET authentifié | | api.post(url, options) | Raccourci POST authentifié | | api.put(url, options) | Raccourci PUT authentifié | | api.patch(url, options) | Raccourci PATCH authentifié | | api.del(url, options) | Raccourci DELETE authentifié | ## Authentification ### Login ```javascript import { useApi, useNavigation } from '@cap-rel/smartcommon'; function LoginPage() { const api = useApi(); const nav = useNavigation(); const handleLogin = async (email, password) => { try { const user = await api.login({ login: email, password: password, rememberMe: true }); console.log('Connecté :', user); nav.navigate('/'); } catch (error) { console.error('Erreur de connexion:', error); alert('Identifiants incorrects'); } }; return (
{ e.preventDefault(); const fd = new FormData(e.target); handleLogin(fd.get('email'), fd.get('password')); }}>
); } ``` ### Logout ```javascript function LogoutButton() { const api = useApi(); const nav = useNavigation(); const handleLogout = async () => { await api.logout(); nav.navigate('/login'); }; return ( ); } ``` ### Accès à l'utilisateur connecté useApi expose directement l'objet utilisateur : ```javascript const api = useApi(); if (api.user) { console.log('Connecté :', api.user.login); console.log('Token expire à :', api.user.tokenExpiry); } ``` ## Requêtes publiques Pour les endpoints sans authentification : ```javascript const api = useApi(); // GET const info = await api.public.get('public/info').json(); // POST const result = await api.public.post('public/contact', { json: { name: 'Jean', message: 'Bonjour' } }).json(); ``` ## Requêtes authentifiées Pour les endpoints protégés par JWT : ```javascript const api = useApi(); // GET - Liste const items = await api.private.get('items').json(); // GET - Détail const item = await api.private.get(`items/${id}`).json(); // POST - Création const newItem = await api.private.post('items', { json: { label: 'Nouveau produit', price: 29.99 } }).json(); // PUT - Modification const updated = await api.private.put(`items/${id}`, { json: { label: 'Produit modifié' } }).json(); // DELETE - Suppression await api.del(`items/${id}`); ``` ## Méthodes raccourcies En plus des méthodes `api.private.get(...).json()`, useApi expose des raccourcis qui gèrent automatiquement le parsing JSON et les erreurs : ```javascript const api = useApi(); // GET - retourne directement les données parsées const items = await api.get('items'); // POST const created = await api.post('items', { json: { label: 'Nouveau' } }); // PUT const updated = await api.put(`items/${id}`, { json: data }); // PATCH const patched = await api.patch(`items/${id}`, { json: { status: 1 } }); // DELETE await api.del(`items/${id}`); ``` ### Option raw Pour récupérer des données binaires (images, fichiers Excel, PDF...) : ```javascript const response = await api.get(`temp-file/${token}/binary`, { raw: true }); const blob = await response.blob(); ``` ### Option silent Supprime la notification automatique en cas d'erreur : ```javascript const data = await api.get('optional-endpoint', { silent: true }); ``` ## Exemple complet : CRUD ```javascript import { useEffect } from 'react'; import { Page, Block, List, ListItem, Button, Spinner } from '@cap-rel/smartcommon'; import { useApi, useStates, useNavigation } from '@cap-rel/smartcommon'; export const ProductsPage = () => { const api = useApi(); const nav = useNavigation(); const st = useStates({ initialStates: { products: [], loading: true, error: null } }); // Charger les produits useEffect(() => { loadProducts(); }, []); const loadProducts = async () => { st.set('loading', true); st.set('error', null); try { const data = await api.private.get('products').json(); st.set('products', data.products); } catch (err) { st.set('error', err.message); } finally { st.set('loading', false); } }; // Supprimer un produit const deleteProduct = async (id) => { if (!confirm('Supprimer ce produit ?')) return; try { await api.del(`products/${id}`); // Retirer de la liste locale st.set('products', st.get('products').filter(p => p.id !== id)); } catch (err) { alert('Erreur : ' + err.message); } }; if (st.get('loading')) { return ; } if (st.get('error')) { return (

Erreur : {st.get('error')}

); } return ( {st.get('products').map(product => ( nav.navigate(`/products/${product.id}`)} actions={ } /> ))} ); }; ``` ## Gestion des erreurs ```javascript const api = useApi(); const createProduct = async (data) => { try { const result = await api.private.post('products', { json: data }).json(); return { success: true, data: result }; } catch (error) { // Erreur HTTP if (error.response) { const status = error.response.status; const body = await error.response.json().catch(() => ({})); if (status === 400) { return { success: false, error: 'Données invalides', details: body }; } if (status === 401) { return { success: false, error: 'Session expirée' }; } if (status === 403) { return { success: false, error: 'Accès interdit' }; } if (status === 404) { return { success: false, error: 'Ressource non trouvée' }; } if (status >= 500) { return { success: false, error: 'Erreur serveur' }; } } // Erreur réseau return { success: false, error: 'Erreur de connexion' }; } }; ``` ## Gestion globale des erreurs (onApiError) Configurez un callback global dans appConfig pour centraliser le traitement des erreurs API : ```javascript // src/appConfig.js import toast from 'react-hot-toast'; export const config = { api: { prefixUrl: import.meta.env.VITE_API_URL, onApiError: (message) => { toast.error(message); }, }, }; ``` Quand une requête échoue via les méthodes raccourcies (get, post, put, patch, del), useApi : - Extrait le message d'erreur du body JSON (champ ''error'' ou ''message'') - Appelle ''onApiError'' avec ce message (sauf si ''silent: true'') - Détecte aussi les erreurs applicatives (HTTP 200 avec champ ''error'' dans le body) ## Fonctionnalités automatiques ### Gestion des tokens JWT - Le token d'accès est ajouté automatiquement aux requêtes `api.private` - Le token est rafraîchi automatiquement avant expiration - En cas d'erreur 401, un refresh est tenté automatiquement ### Headers automatiques Chaque requête inclut : - `Authorization: Bearer ` (requêtes private) - `X-DEVICEID: ` (identification de l'appareil) - `Content-Type: application/json` ### Circuit breaker Protection contre les requêtes en cascade : - Blocage temporaire après plusieurs échecs - Détection automatique de la connexion internet ## Paramètres de requête ### Query string ```javascript // GET /products?category=electronics&limit=10 const products = await api.private.get('products', { searchParams: { category: 'electronics', limit: 10 } }).json(); ``` ### Headers personnalisés ```javascript const data = await api.private.get('items', { headers: { 'X-Custom-Header': 'value' } }).json(); ``` ### Timeout personnalisé ```javascript const data = await api.private.get('slow-endpoint', { timeout: 60000 // 60 secondes }).json(); ``` ## Points clés à retenir 1. **api.login()** gère automatiquement le stockage des tokens 2. **api.private** ajoute automatiquement l'authentification 3. **api.public** pour les endpoints sans auth 4. Terminer par **.json()** pour parser la réponse 5. Le **refresh token** est géré automatiquement 6. **api.get/post/put/patch/del** : raccourcis avec gestion d'erreur automatique 7. **api.user** : accès direct à l'utilisateur connecté 8. **onApiError** : centralise la notification des erreurs 9. **{ raw: true }** pour les fichiers binaires 10. **{ silent: true }** pour supprimer les notifications d'erreur [[:15_training:module7-smartcommon-hooks:start|← Retour au module]] | [[:15_training:module7-smartcommon-hooks:etat|Chapitre suivant : Gestion d'état →]]