La couche API moderne : GraphQL, REST et tRPC
Le débat est terminé. Vous avez besoin de la sécurité des types. Comment concevoir une couche API évolutive qui ne casse pas le frontend lorsque le backend éternue.
Pourquoi Maison Code en parle
Chez Maison Code Paris, nous agissons comme la conscience architecturale de nos clients. Nous héritons souvent de stacks “modernes” construites sans compréhension fondamentale de l’échelle.
Nous abordons ce sujet car il représente un point de pivot critique dans la maturité de l’ingénierie. Une mise en œuvre correcte différencie un MVP fragile d’une plateforme résiliente de niveau entreprise.
Le problème « Undéfini n’est pas une fonction »
Pendant 20 ans, les développeurs Frontend et Backend ont vécu en silos.
Backend Dev : “J’ai mis à jour l’API utilisateur. Elle renvoie fullName au lieu de first_name.”
Développeur frontend : “D’accord.” (Oublie de mettre à jour le code).
Production : Crash. user.first_name n’est pas défini.
C’est ce qu’on appelle le écart d’intégration.
La documentation (Swagger/OpenAPI) aide, mais la documentation ment. Cela devient obsolète.
La solution moderne est la sécurité de type de bout en bout.
Le code lui-même empêche ces erreurs de non-concordance.
Pourquoi Maison Code discute de l’architecture API
Chez Maison Code, nous héritons de projets où la couche API est un désordre de points de terminaison spaghetti.
/api/v1/get-user-final-final-2.
Cela ralentit considérablement la vitesse des fonctionnalités.
Nous implémentons des architectures Type-Safe.
Nous activons l’autonomie frontale. L’équipe front-end ne devrait pas avoir à déranger l’équipe back-end pour ajouter un champ à une réponse JSON.
Nous écrivons à ce sujet parce que les équipes évolutives fonctionnent sur des contrats évolutifs.
Si votre schéma est lâche, votre produit est lâche.
1. Les prétendants
1. REST (avec OpenAPI)
Le classique.
- Avantages : simple, pouvant être mis en cache (HTTP 200), universel.
- Inconvénients : surrécupération (obtention de trop de données) et sous-récupération (n+1 requêtes).
- Modern Twist : utilisez OpenAPI (Swagger) pour générer automatiquement des types TypeScript.
npx openapi-typescript schema.json -o schema.d.ts. Désormais, si le backend change, la construction du frontend échoue.
2. GraphQL
L’utilisateur expérimenté.
- Avantages : Le client demande exactement ce dont il a besoin.
requête { utilisateur { nom, avatar (taille : PETIT) } } - Inconvénients : mise en cache complexe (tout est POST 200), complexité (résolveurs, chargeurs de données).
- Meilleur pour : applications complexes et riches en graphiques (réseaux sociaux, tableaux de bord) et intégrations Headless CMS (Shopify, Contentful).
3. tRPC (Appel de procédure à distance TypeScript)
Le démon de la vitesse.
- Contexte : si vous possédez à la fois le frontend et le backend (par exemple, Next.js Monorepo).
- Magic : Vous importez la fonction backend directement dans le code frontend.
// Back-end export const appRouter = routeur ({ getUser : publicProcedure.query(() => { identifiant : 1, nom : 'Alex' }) }); // Front-end const utilisateur = trpc.getUser.useQuery(); console.log(utilisateur.data.name); // Tapé ! - Il n’existe pas de schéma API. Le code est le schéma.
- Inconvénients : verrouillé dans les monorepos TypeScript.
2. Modèle : Le BFF (Backend pour Frontend)
Dans les architectures microservices, vous ne voulez pas que le frontend appelle 10 services distincts (service utilisateur, service panier, service produit). C’est lent (10 allers-retours) et compliqué. Solution : une couche BFF (généralement des routes API GraphQL ou Next.js). Le Frontend appelle le BFF une fois. Le BFF appelle les 10 services, regroupe les données, supprime les secrets et renvoie un JSON propre. Cela permet au Frontend d’être “stupide” et au Backend d’être “complexe” sans nuire aux performances.
3. Validation des entrées : Zod
Ne faites jamais confiance au client. Que vous utilisiez REST ou GraphQL, vous devez valider les entrées. Zod est la norme.
importer { z } depuis 'zod' ;
const UserSchema = z.object({
email : z.string().email(),
âge : z.number().min(18)
});
app.post('/user', (req, res) => {
résultat const = UserSchema.safeParse(req.body);
si (!result.success) {
return res.status(400).json(result.error);
}
// Continuer en toute sécurité
});
Ce schéma Zod peut être partagé avec le frontend pour générer automatiquement une logique de validation de formulaire (en utilisant react-hook-form).
4. La couche fédération (Apollo)
Pour les applications Enterprise, un seul serveur GraphQL ne suffit pas. Vous avez la « Product Team » à Berlin et la « Checkout Team » à New York. Ils ne peuvent pas partager une seule base de code. Solution : Fédération GraphQL. Chaque équipe construit son propre sous-graphe. Une « passerelle » les rassemble en un seul « Supergraph ». Le Frontend interroge la passerelle. Cela ressemble à une seule API, mais elle est alimentée par 50 microservices. C’est l’architecture de Netflix et Airbnb.
5. Stratégies de gestion des erreurs
“200 OK” mais “erreurs : [“Non trouvé”]`. C’est le piège GraphQL. Stratégie :
- Erreurs réseau : (DNS, 500 s). Réessayez avec une interruption exponentielle.
- Erreurs logiques : (Utilisateur introuvable). Renvoie un type nullable ou un type Union.
Cela oblige le frontend à gérer explicitement le cas d’erreur dans le code.union UserResult = Utilisateur | UtilisateurNotFound | Autorisation refuséeif (result.__typename === 'UserNotFound') ...Gestion des erreurs de type sécurisé.
6. Stratégies de mise en cache (périmées pendant la revalidation)
REST se met facilement en cache via les en-têtes HTTP (Cache-Control : max-age=3600).
GraphQL est plus difficile.
Nous utilisons Stale-While-Revalidate (SWR) sur le client (TanStack Query).
- Afficher instantanément les données mises en cache (périmées).
- Récupérez de nouvelles données en arrière-plan (Revalider).
- Mettez à jour l’interface utilisateur si elle est modifiée. Cela donne l’impression que l’application est « instantanée », même si le réseau est lent.
7. La couche de sécurité (limitation de débit et JWT)
Une API sans sécurité est une porte ouverte. Limitation de débit : empêchez les DDoS.
- Utilisez
upstash/ratelimit(Redis) sur Edge. - “10 requêtes toutes les 10 secondes par IP”. Authentification :
- Arrêtez d’utiliser les cookies pour les API. Utilisez JWT (JSON Web Tokens) dans l’en-tête « Authorization : Bearer ». *Apatride. Évolutif.
- Mais assurez-vous de gérer la rotation des jetons (refresh tokens) en toute sécurité. Validation :
- Désinfectez les entrées contre l’injection SQL (utilisez les ORM).
- Désinfectez les sorties contre XSS.
8. Intégration de la migration héritée (The Strangler Fig)
Vous disposez d’une API Legacy monolithique (Java/PHP). Vous voulez une API Node.js moderne. Ne réécrivez pas tout d’un coup. Vous échouerez. Utilisez le Modèle de figue étrangleur.
- Mettez un proxy (Nginx / Cloudflare) devant.
- Acheminez
/api/v1/usersvers la nouvelle API. - Acheminez tout le reste vers l’API héritée.
- Migrez lentement les points de terminaison un par un.
- Désactivez l’API héritée lorsque le trafic tombe à zéro. Cela vous permet d’expédier de la valeur immédiatement sans réécriture « Big Bang ».
9. Docs as Code (Stripe Standard)
Comment Stripe fait-il pour que ses documents soient si bons ? Ils les génèrent à partir du code. N’écrivez pas de documents API dans Word ou Confluence. Écrivez-les dans le Schéma.
- OpenAPI : ajoutez des champs « description » à votre YAML.
- GraphQL : Ajoutez
""" docstrings """à votre schéma. - Outils : utilisez Scalar ou Redoc pour restituer de superbes documents HTML à partir du schéma.
- CI/CD : déployez automatiquement les documents à chaque fusion. Les documents obsolètes sont pires que l’absence de documents.
10. Le point de vue du sceptique
“GraphQL est mort. Utilisez simplement fetch.”
Contre-point :
GraphQL est bien vivant dans l’entreprise.
Shopify, GitHub et Facebook fonctionnent dessus.
“Juste récupérer” fonctionne pour les blogs.
Cela ne fonctionne pas pour une page de produit de commerce électronique qui nécessite le statut de prix, de variantes, d’inventaire, d’avis, de recommandations et de liste de souhaits de l’utilisateur dans une seule demande.
Si vous faites cela avec REST, vous avez soit 6 requêtes (lent), soit un point de terminaison monstrueux /get-product-page-data (inmaintenable).
GraphQL résout le problème d’Orchestration.
11. Trafic interne : tampons de protocole (gRPC)
JSON est lisible par l’homme. C’est aussi lent.
Il répète les clés : {"name": "alex", "age": 10}. “nom” est envoyé sur le fil à chaque fois.
gRPC utilise Protobuf (binaire).
Il envoie « 0x12 0x04 0x61 0x6c 0x65 0x78 ».
Il est 30 % plus petit et 5 fois plus rapide à analyser.
Nous utilisons gRPC pour la communication interne de service à service (microservices).
Nous utilisons GraphQL/JSON pour la communication client-serveur.
Le bon outil pour le bon travail.
12. Le problème N+1 (chargeurs de données)
Le tueur classique de GraphQL.
Requête : utilisateurs { posts { title } }.
- 1 requête pour les utilisateurs (SELECT * FROM utilisateurs).
- 100 requêtes pour les publications (SELECT * FROM posts WHERE user_id = 1… 100).
- Total : 101 appels DB.
Solution : DataLoader.
Il regroupe les 100 requêtes en UNE.
SELECT * FROM posts WHERE user_id IN (1, 2, ... 100). Cela réduit la charge de la base de données de 99 %. Si vous n’utilisez pas de DataLoaders, votre serveur GraphQL fondra.
13. Conclusion
La couche API est le système nerveux de votre application. S’il est faible (non typé, non testé), le corps échoue. S’il est fort (tRPC, GraphQL Federation), le corps bouge avec agilité. Arrêtez de deviner correctement les noms. Commencez à les appliquer. Contrat d’abord. Code deuxième.
API cassant le Frontend ?
Nous concevons les couches API Type-Safe à l’aide de GraphQL et tRPC pour garantir l’absence de bogues d’intégration.