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.login(body) Connexion utilisateur
api.logout() Déconnexion
api.public Instance pour requêtes publiques
api.private Instance pour requêtes authentifiées

Authentification

Login

snippet.javascript
import { useApi, useNavigation } from '@cap-rel/smartcommon';
 
function LoginPage() {
    const api = useApi();
    const navigate = useNavigation();
 
    const handleLogin = async (email, password) => {
        try {
            const user = await api.login({
                login: email,
                password: password,
                rememberMe: true
            });
 
            console.log('Connecté :', user);
            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 navigate = useNavigation();
 
    const handleLogout = async () => {
        await api.logout();
        navigate('/login');
    };
 
    return (
        <button onClick={handleLogout}>Déconnexion</button>
    );
}

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.private.delete(`items/${id}`);

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 navigate = 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.private.delete(`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={() => 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={() => 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' };
    }
};

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

← 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
    • Requêtes publiques
    • Requêtes authentifiées
    • Exemple complet : CRUD
    • Gestion des erreurs
    • 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
    • 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" quasiment 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
    • 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