Swelv· Article 2 sur 2
Flutter et GraphQL : un client typé, et des versions qui cohabitent
Un client généré depuis le schéma, des écrans qui demandent exactement leurs champs, et un serveur qui continue de parler aux versions déjà installées.
5 min de lectureRead in English
Sur le web, un déploiement met tout le monde à jour. Sur mobile, non : chaque version publiée reste installée sur des téléphones pendant des semaines, parfois des mois, et toutes parlent au même serveur.
Pourquoi Flutter, pourquoi GraphQL
Swelv existe sur iOS et Android, avec une petite équipe. Flutter permet d'écrire l'app une seule fois, en Dart, et de la compiler pour les deux plateformes, avec des animations fluides et le même rendu partout. Une fonctionnalité se développe une fois, se teste une fois, et sort le même jour sur les deux stores.
Côté API, GraphQL répond à un problème typique du mobile. Avec une API REST, chaque écran reçoit ce que l'endpoint a prévu de renvoyer : trop de données, ou pas assez, et un deuxième appel. Avec GraphQL, il n'y a qu'un point d'entrée, et chaque écran décrit exactement les champs qu'il veut, en un seul aller-retour. Sur un réseau mobile, ça se sent.
Trop de données, et deux allers-retours.
livre {
titre auteur couverture
amis { prénom photo }
}titreauteurcouvertureprénomphotoExactement ce que l'écran affiche, en un aller-retour.
Le défi. Faire évoluer l'API sans arrêt, sans jamais casser une app déjà installée que son propriétaire ne mettra pas à jour avant longtemps.
Un client typé, généré
Le schéma GraphQL décrit tous les types que le serveur sait servir. Il est généré depuis le code du serveur, puis l'app s'en sert pour générer à son tour ses classes Dart. Plus de JSON lu à la main : chaque requête est une classe, chaque champ a son type, et une faute de frappe ne compile pas.
query Accueil { livres { titre auteur couverture }}
final res = await client .request(GAccueilReq()) .first;final livre = res.data?.livres.first;livre?.titre; // Stringlivre?.prix; // ✗ ne compile pas
Chaque écran demande ses champs
C'est ce qui rend GraphQL précieux sur mobile : une version ne reçoit que ce qu'elle demande. Un champ ajouté ne la dérange pas, elle ne le voit même pas. Et côté serveur, on ne lit en base que les colonnes réellement demandées.
Accueil
★★★★☆
livres { titre couverture}
Accueil
★★★★☆
livres { titre couverture note}
Le piège : les valeurs d'enum
Ajouter un champ est sans risque. Ajouter une valeur à une liste fermée (une enum) ne l'est pas : le code généré d'une ancienne version ne connaît que les valeurs de son époque, et une valeur inconnue fait échouer toute la réponse. L'app envoie donc sa version à chaque requête, et le serveur adapte ce qu'il répond aux versions trop anciennes.
La même réponse, pour deux versions
l'app envoie sa version{ "statut": "EN_PAUSE", "émotions": ["ÉMU", "AMUSÉ"]}
{ "statut": "EN_COURS", "émotions": ["ÉMU"]}
L'ancienne app affiche moins, mais elle affiche.
Ajouter, déprécier, puis seulement retirer
Retirer quelque chose de l'API, c'est le retirer à toutes les versions installées en même temps. D'où une règle en trois temps :
- 01AjouterUn champ ou une requête facultatifs : sans risque, personne ne les demande encore.
- 02DéprécierLe remplaçant à côté, l'ancien marqué comme déprécié, et gardé.
- 03RetirerSeulement quand plus aucune version installée ne s'en sert : mise à jour imposée, ou statistiques de versions.
Une seule requête vit hors du client généré : celle qui demande s'il faut mettre l'app à jour. Elle doit fonctionner même quand tout le reste a changé.
Ce que j'en retiens
- Générer le client depuis le schéma. Le compilateur relit chaque changement d'API.
- Une version installée est un client qu'on ne contrôle plus. On lui envoie ce qu'il comprend.
- Ajouter est gratuit, retirer se mérite.