đ€ GraphQL devrait-il ĂȘtre diffĂ©rent pour diffĂ©rents utilisateurs ?
GraphQL est une interface pour rĂ©cupĂ©rer des donnĂ©es d'une source, la spec GraphQL dĂ©finissant les exigences pour cette interface. Tant que ces exigences sont satisfaites, GraphQL ne se soucie pas de la façon dont c'est accompli. Le serveur GraphQL peut alors ĂȘtre implĂ©mentĂ© en JavaScript en utilisant des promises, en utilisant une architecture concurrente basĂ©e sur Golang, mappĂ© Ă un fichier Excel, ou autre, et tout cela peut constituer des implĂ©mentations valides de la spec GraphQL.

La façon dont le moteur du serveur est implĂ©mentĂ© n'est pas importante pour l'exĂ©cution rĂ©ussie d'une requĂȘte GraphQL, car l'interaction entre le client et le serveur est toujours la mĂȘme : elle se dĂ©roule en soumettant une requĂȘte GraphQL en utilisant une syntaxe dĂ©finie, et en obtenant une rĂ©ponse correspondante au format JSON.
Maintenant, quand je dis que l'implĂ©mentation n'est pas importante, je le dis du point de vue de l'utilisateur de l'API, qui cherche simplement Ă obtenir des donnĂ©es du serveur. La façon dont les donnĂ©es retournĂ©es ont Ă©tĂ© produites ne prĂ©sente aucun intĂ©rĂȘt.
Mais la situation change pour le développeur cÎté serveur qui travaille sur l'API, pour qui les détails de l'implémentation sont en effet trÚs importants. Si je code mon API GraphQL en PHP, je ferai de mon mieux pour que mon API soit résolue aussi efficacement que possible, et pour avoir une conception architecturale aussi élégante que possible, en utilisant les capacités offertes par PHP.

Nous avons donc un possible conflit d'intĂ©rĂȘts entre la nĂ©cessitĂ© de protĂ©ger l'API et les capacitĂ©s attendues par les dĂ©veloppeurs travaillant sur l'API, qui ne veulent pas se voir retirer des fonctionnalitĂ©s supportĂ©es par le langage sous-jacent (comme la possibilitĂ© d'exĂ©cuter du code rĂ©cursif).
Ce conflit est devenu évident dans l'issue #929: Allow recursive references in fragments, qui soutient que GraphQL ne devrait pas interdire les récursions dans les fragments.
Lors d'une réunion passée du groupe de travail GraphQL, Roman, le développeur qui a soulevé l'issue, a exprimé pourquoi il est en désaccord avec la limitation imposée par la spec :
Je suis dĂ©veloppeur cĂŽtĂ© serveur, et j'ai l'impression que la spec parle trop de l'exĂ©cution cĂŽtĂ© serveur, alors qu'elle devrait se concentrer sur ce que le client veut recevoir â pas sur comment
La rĂšgle interdisant les rĂ©cursions dans les fragments a Ă©tĂ© justifiĂ©e sur la prĂ©misse de maintenir l'API publique sĂ©curisĂ©e. AprĂšs tout, GraphQL a Ă©tĂ© créé par Facebook pour fournir des donnĂ©es Ă son application publique, et les utilisateurs ne devraient pas ĂȘtre capables d'exploiter une faille dans la conception de l'API qui pourrait faire tomber le service.
Le créateur de GraphQL, Lee Byron, a exprimé trois préoccupations majeures :
rĂ©cursion infinie ; les limitations ne seraient pas seulement une spĂ©cification â comment et quand devrait-elle s'arrĂȘter
validation des donnĂ©es ; retourner la mĂȘme valeur plusieurs fois, comment est-ce reprĂ©sentĂ© dans les donnĂ©es. IdĂ©alement, vous souhaitez dĂ©tecter qu'elle est cyclique et arrĂȘter immĂ©diatement, mais certains serveurs ne peuvent pas dĂ©tecter cela et peuvent boucler de nombreuses fois avant de dĂ©tecter que quelque chose a mal tournĂ© et de s'arrĂȘter
quel est le coĂ»t de ne pas avoir cela ; est-ce que cela justifie ces problĂšmes ? Non ; il est toujours possible de spĂ©cifier le nombre de niveaux de profondeur dans votre requĂȘte â c'est effectivement la version dĂ©simbriquĂ©e de ce que nous ferions si nous gĂ©rions cela dans GraphQL
Venant de leurs propres perspectives, Roman et Lee ont tous les deux raison. Lee Byron s'inquiĂšte de la sĂ©curitĂ© de l'API GraphQL publique. Ăviter les fragments rĂ©cursifs est justifiĂ© pour s'assurer qu'aucun acteur malveillant ne puisse faire tomber le systĂšme en exĂ©cutant une boucle cyclique sans fin dans la requĂȘte, et mĂȘme Ă©liminer le risque d'un « self-DDoSing » d'Ă©quipe, ce qui pourrait se produire si une requĂȘte bloquant le systĂšme est publiĂ©e involontairement.
Roman, cependant, est prĂ©occupĂ© par les limitations Ă ses propres capacitĂ©s pour crĂ©er une API GraphQL. Comme Roman peut ĂȘtre le seul consommateur de son API (c'est-Ă -dire une API privĂ©e qui n'est pas exposĂ©e aux utilisateurs), ou parce que son serveur peut avoir la capacitĂ© de dĂ©tecter et d'arrĂȘter les cycles rĂ©cursifs, il estime que la limitation de GraphQL est prĂ©judiciable et injustifiable.
Au cĆur de la discussion, le problĂšme n'est pas de savoir si les fragments rĂ©cursifs devraient ĂȘtre autorisĂ©s ou non, mais quelque chose de plus fondamental : qui est la cible de GraphQL ? Si ce n'est pas un seul groupe, une seule spĂ©cification d'API pourrait-elle satisfaire les exigences de tous les diffĂ©rents acteurs ? Et si le conflit ne peut ĂȘtre Ă©vitĂ©, peut-il au moins ĂȘtre remĂ©diĂ© d'une façon ou d'une autre ?
Explorons ces questions.
Qui est la cible de GraphQL ?
GraphQL est utilisé par différents types d'acteurs, parmi lesquels nous pouvons identifier :
1. Utilisateurs de l'API : Ceux qui consomment des donnĂ©es d'un endpoint GraphQL, pour quelque raison que ce soit. Par exemple, nous pouvons tous ĂȘtre des utilisateurs de l'API GraphQL publique de GitHub, pour rĂ©cupĂ©rer des donnĂ©es concernant nos dĂ©pĂŽts GitHub.
2. Développeurs cÎté client : Ceux qui créent des applications cÎté client alimentées par un endpoint GraphQL. Par exemple, les développeurs qui construisent des sites avec Gatsby s'appuient sur GraphQL pour récupérer le contenu du site.
3. Développeurs backend : Ceux qui créent les resolvers pour l'API GraphQL.
De plus, nous devons noter que l'API GraphQL peut ĂȘtre publique ou privĂ©e :
API publique : Comme tout le monde a accÚs à l'endpoint GraphQL, nous devons nous préoccuper des mesures de sécurité pour éviter les attaques d'acteurs malveillants.
API privĂ©e : Comme seuls les acteurs prĂ©vus se voient accorder l'accĂšs Ă l'API, il n'y a pas de risques de sĂ©curitĂ© inhĂ©rents, et le self-DDoSing peut ĂȘtre facilement Ă©vitĂ© avec de bonnes pratiques de codage.
Une seule spécification d'API satisfait-elle les exigences de tous les acteurs ?
L'issue soulevĂ©e par Roman peut ĂȘtre interprĂ©tĂ©e ainsi : « Si mon API GraphQL est privĂ©e, et que je sais exactement ce que je fais (ayant une certitude Ă 100 % que mon code fonctionnera comme prĂ©vu et qu'aucune exĂ©cution bloquante ne sera produite), alors pourquoi ne puis-je pas utiliser de rĂ©cursions dans les fragments ? »

Un exemple de cette situation se produit chaque fois que nous utilisons un framework alimenté par GraphQL pour construire des sites statiques (comme Gatsby, Next.js ou RedwoodJS), car l'API GraphQL sera souvent privée, et nous ne pouvons pas faire accidentellement un DDoS de notre application et subir des conséquences négatives (au pire, elle plantera lors de la construction du site statique dans un environnement de développement ou de staging).
Les développeurs utilisant la configuration ci-dessus pourraient tout à fait se demander pourquoi la spec GraphQL leur interdit d'utiliser des fonctionnalités bénéfiques, qui n'ont aucune conséquence négative pour leur configuration.
En conclusion, en interdisant les fragments rĂ©cursifs, la spec GraphQL impose une mesure de sĂ©curitĂ© qui s'applique Ă une sĂ©lection de tous les usages potentiels de GraphQL, pas Ă tous, afin d'ĂȘtre du cĂŽtĂ© sĂ»r.
La spec GraphQL pourrait-elle mieux satisfaire tous les acteurs ?
Si différents acteurs ont des exigences différentes, comment la spec GraphQL peut-elle les satisfaire tous ? (L'idée est d'éviter de forker la spec et de produire des versions personnalisées pour des cibles spécifiques.)
Explorons quelques idées, la premiÚre devant passer par le processus de contribution à la spec, tandis que la seconde ne le devrait pas.
Feature-toggle au niveau de la spec GraphQL
Une voie possible est que la spec « suggĂšre » mais n'« impose » pas de rĂšgles. Dans ce cas, la rĂšgle interdisant les rĂ©cursions dans les fragments pourrait ĂȘtre fortement suggĂ©rĂ©e, mais la fonctionnalitĂ© serait tout de mĂȘme acceptĂ©e.
Or, cette solution changerait l'état par défaut des fragments récursifs de « obligatoire » à « optionnel », ce qui produirait deux conséquences négatives :
- L'API serait non sécurisée par défaut (le scénario que Lee Byron veut éviter)
- Cela produirait un breaking change, car une requĂȘte interdite serait alors autorisĂ©e
Il serait alors prĂ©fĂ©rable d'inverser l'option, en maintenant les rĂ©cursions dans les fragments toujours interdites par dĂ©faut mais en donnant la possibilitĂ© d'activer un feature-flag qui dĂ©sactive ce comportement. Comme la fonctionnalitĂ© doit ĂȘtre explicitement dĂ©sactivĂ©e, seuls les administrateurs qui savent ce qu'ils font le feront.
Comme la fonctionnalité est plus précieuse dans certaines configurations, les serveurs et frameworks GraphQL pourraient décider si/comment/quand proposer la configuration. Par exemple, Gatsby pourrait afficher de maniÚre visible l'option via une interface lors de la création de sites statiques, et la masquer autrement.
L'idĂ©e gĂ©nĂ©rale est que la spec GraphQL supporte des « fonctionnalitĂ©s activĂ©es mais optionnelles », pouvant ĂȘtre activĂ©es/dĂ©sactivĂ©es via la configuration, et dont l'Ă©tat par dĂ©faut est celui qu'elles ont dĂ©jĂ dans la spec.
Interdire les fragments récursifs en serait une, et il pourrait y avoir d'autres fonctionnalités similaires, comme un type Map, qui n'a pas été accepté dans la spec par Lee Byron parce que :
Il y a des compromis significatifs entre un type Map et une liste de paires clé/valeur. Un problÚme est la pagination sur la collection. Les listes de valeurs peuvent avoir des rÚgles de pagination claires, tandis que les Maps qui ont souvent des paires clé-valeur non ordonnées sont beaucoup plus difficiles à paginer.
Un autre problĂšme est l'utilisation. La plupart du temps, Map est utilisĂ© dans les APIs oĂč un champ de la valeur est indexĂ©, ce qui est Ă mon avis un anti-pattern d'API car l'indexation est un problĂšme de stockage et un problĂšme de mise en cache cĂŽtĂ© client mais pas un problĂšme de transport. Cet anti-pattern me prĂ©occupe. Bien qu'il y ait de bonnes utilisations pour Maps dans les APIs, je crains que l'utilisation courante soit pour ces anti-patterns, je suggĂšre donc de procĂ©der avec prudence.
Lee Byron a exprimĂ© sa crainte que la fonctionnalitĂ© soit utilisĂ©e comme un anti-pattern. Cependant, il a Ă©galement reconnu qu'il en existe de bonnes utilisations. Alors, comme l'issue a recueilli beaucoup de soutien de la communautĂ© (avec plus de 150 đ), les dĂ©veloppeurs pourraient se voir donner la possibilitĂ© d'activer explicitement l'ajout d'un type Map Ă leurs schĂ©mas, et d'en gĂ©rer les consĂ©quences.
Feature-toggle par les serveurs GraphQL
Si la proposition ci-dessus ne recueille pas de soutien car elle est trop risquée pour la spec GraphQL, une alternative est de l'implémenter au niveau du serveur GraphQL. Les serveurs GraphQL pourraient alors fournir une fonctionnalité personnalisée qui désactive les récursions dans les fragments.
En gĂ©nĂ©ralisant l'idĂ©e, les serveurs GraphQL pourraient proposer de dĂ©sactiver certaines fonctionnalitĂ©s de la spec, et d'en activer d'autres qui manquent Ă la spec. Pour que ce comportement ne produise pas de surprises, les serveurs doivent s'assurer que l'Ă©tat par dĂ©faut est celui requis par la spec, et l'administrateur de l'API doit ĂȘtre pleinement conscient des consĂ©quences de l'activation de la fonctionnalitĂ©. (C'est la stratĂ©gie suivie par Gato GraphQL pour ses « innovative features ».)
Conclusion
Alors que GraphQL est devenu de plus en plus populaire, de nouveaux frameworks supportant de nouvelles capacités en ont fait partie de leur stack, et de nouveaux acteurs (et de nouveaux types d'acteurs) se sont impliqués. Une spécification initialement créée par Facebook pour définir comment ses applications obtiendraient des données de ses serveurs doit de plus en plus faire face à davantage de cas d'usage.
Il est inĂ©vitable que des conflits surviennent, oĂč un ensemble d'acteurs a besoin d'une fonctionnalitĂ© qui est contre-productive, voire prĂ©judiciable, pour d'autres acteurs, comme c'est le cas avec les fragments rĂ©cursifs. Que peut-on faire pour amĂ©liorer la situation et Ă©viter que les acteurs insatisfaits ne soient déçus par GraphQL ?
J'ai soutenu que la spec pourrait offrir la possibilitĂ© de « dĂ©sactiver » une fonctionnalitĂ©, permettant aux administrateurs qui savent ce qu'ils font de supprimer certaines limitations afin de satisfaire leurs propres exigences. Maintenant, je ne suis pas moi-mĂȘme d'accord avec cette solution, mais je la mets sur la table nĂ©anmoins, car cette discussion doit avoir lieu. Comme cette idĂ©e est controversĂ©e, une meilleure alternative est que les serveurs GraphQL fournissent ce comportement via des fonctionnalitĂ©s personnalisĂ©es, qui doivent ĂȘtre explicitement activĂ©es.