SmarMaker - Documentation
Docs» 15_training:module7-smartcommon-hooks:useapi

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 :

snippet.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

snippet.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

snippet.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 (
        <form onSubmit={(e) => {
            e.preventDefault();
            const fd = new FormData(e.target);
            handleLogin(fd.get('email'), fd.get('password'));
        }}>
            <input name="email" type="email" placeholder="Email" />
            <input name="password" type="password" placeholder="Mot de passe" />
            <button type="submit">Connexion</button>
        </form>
    );
}

Logout

snippet.javascript
function LogoutButton() {
    const api = useApi();
    const nav = useNavigation();
 
    const handleLogout = async () => {
        await api.logout();
        nav.navigate('/login');
    };
 
    return (
        <button onClick={handleLogout}>Déconnexion</button>
    );
}

Accès à l'utilisateur connecté

useApi expose directement l'objet utilisateur :

snippet.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 :

snippet.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 :

snippet.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 :

snippet.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…) :

snippet.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 :

snippet.javascript
const data = await api.get('optional-endpoint', { silent: true });

Exemple complet : CRUD

snippet.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 <Page title="Produits"><Spinner /></Page>;
    }
 
    if (st.get('error')) {
        return (
            <Page title="Produits">
                <Block>
                    <p>Erreur : {st.get('error')}</p>
                    <Button onClick={loadProducts}>Réessayer</Button>
                </Block>
            </Page>
        );
    }
 
    return (
        <Page title="Produits" onRefresh={loadProducts}>
            <Block>
                <Button onClick={() => nav.navigate('/products/new')}>
                    Nouveau produit
                </Button>
            </Block>
 
            <Block>
                <List>
                    {st.get('products').map(product => (
                        <ListItem
                            key={product.id}
                            title={product.label}
                            subtitle={`${product.price} €`}
                            onClick={() => nav.navigate(`/products/${product.id}`)}
                            actions={
                                <Button
                                    size="sm"
                                    variant="danger"
                                    onClick={(e) => {
                                        e.stopPropagation();
                                        deleteProduct(product.id);
                                    }}
                                >
                                    Supprimer
                                </Button>
                            }
                        />
                    ))}
                </List>
            </Block>
        </Page>
    );
};

Gestion des erreurs

snippet.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 :

snippet.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 <accessToken> (requêtes private)
  • X-DEVICEID: <uuid> (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

snippet.javascript
// GET /products?category=electronics&limit=10
const products = await api.private.get('products', {
    searchParams: {
        category: 'electronics',
        limit: 10
    }
}).json();

Headers personnalisés

snippet.javascript
const data = await api.private.get('items', {
    headers: {
        'X-Custom-Header': 'value'
    }
}).json();

Timeout personnalisé

snippet.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

← Retour au module | Chapitre suivant : Gestion d'état →

Previous Next

Made with ❤ by CAP-REL · SmartMaker · GNU AGPL v3+
Code source · Faire un don
SmarMaker - Documentation
Traductions de cette page:
  • Français
  • Deutsch
  • English
  • Español
  • Italiano
  • Nederlands

Table of Contents

Table des matières

  • Chapitre 1 : useApi
    • Configuration
    • Structure retournée
    • Authentification
      • Login
      • Logout
      • Accès à l'utilisateur connecté
    • Requêtes publiques
    • Requêtes authentifiées
    • Méthodes raccourcies
      • Option raw
      • Option silent
    • Exemple complet : CRUD
    • Gestion des erreurs
    • Gestion globale des erreurs (onApiError)
    • Fonctionnalités automatiques
      • Gestion des tokens JWT
      • Headers automatiques
      • Circuit breaker
    • Paramètres de requête
      • Query string
      • Headers personnalisés
      • Timeout personnalisé
    • Points clés à retenir
  • SmartAuth
  • SmartMaker - Back (PHP)
    • Mapping Dolibarr - React
  • SmartMaker - Front (React)
    • Animations de pages
    • Architecture
    • Astuces
    • Calendar
    • Composants et pages
    • Configuration du Provider
    • Debug et Logs
    • Hooks SmartCommon
    • PWA (Progressive Web App)
    • Requêtes API
    • Routage
    • SmartCommon
    • Stockage de données
    • Synchronisation offline
    • Thèmes
    • Traductions
  • HowTo - Pas à pas - Votre première application
    • Développement PHP (back)
    • Développement React (front)
    • Première étape : Module Builder Dolibarr
    • SmartAuth
    • SmartBoot : Un squelette prêt à l'emploi
  • Formation SmartMaker
    • Module 1 : Fondamentaux JavaScript ES6+
      • Chapitre 1 : Variables et Scope
      • Chapitre 2 : Fonctions
      • Chapitre 3 : Programmation Asynchrone
      • Chapitre 4 : Modules ES6
    • Module 2 : Introduction à React
      • Chapitre 1 : Philosophie React
      • Chapitre 2 : JSX
      • Chapitre 3 : Composants
    • Module 3 : Hooks React Fondamentaux
      • Chapitre 1 : useState
      • Chapitre 2 : useEffect
      • Chapitre 3 : useRef
      • Chapitre 4 : useContext
    • Module 4 : React Avancé
      • Chapitre 1 : useCallback et useMemo
      • Chapitre 2 : Custom Hooks
      • Chapitre 3 : Redux et Redux Toolkit
    • Module 5 : Architecture SmartMaker
      • Chapitre 1 : Structure du projet
      • Chapitre 2 : Configuration
      • Chapitre 3 : Flux de données
    • Module 6 : SmartCommon - Composants
      • Chapitre 1 : Mise en page
      • Chapitre 2 : Navigation
      • Chapitre 3 : Formulaires
      • Chapitre 4 : Affichage
    • Module 7 : SmartCommon - Hooks
      • Chapitre 1 : useApi
      • Chapitre 2 : Gestion d'état
      • Chapitre 3 : Hooks utilitaires
      • Chapitre 4 : Synchronisation Offline
    • Module 8 : Backend API (PHP)
      • Chapitre 1 : Routage
      • Chapitre 2 : Controllers
      • Chapitre 3 : Mappers
      • Extrafields et formulaires dynamiques
    • Module 9 : Intégration complète
      • Chapitre 1 : Backend
      • Chapitre 2 : Frontend
      • Chapitre 3 : Déploiement
    • Module 10 : Fonctionnalités avancées
      • Chapitre 1 : Mode offline
      • Chapitre 2 : Internationalisation (i18n)
      • Chapitre 3 : Autres fonctionnalités
    • Module 11 : Bonnes pratiques
  • Démonstration
  • Start
  • Composants et pages
  • Afficher le texte source
  • Anciennes révisions
  • Liens de retour
  • Haut de page