Serveur GraphQL code-first
Le schéma GraphQL définit les contrats d'un service GraphQL, en exposant l'ensemble des types, des champs et des mutations qui peuvent être exécutés contre le service. Lors de la création d'un service GraphQL, nous pouvons choisir entre :
- faire du schéma la source unique de vérité et adapter tout notre code d'implémentation à ses définitions
- faire de notre code la source unique de vérité et générer le schéma comme un artefact à partir du code
Dans les deux cas, nous disposerons d'un service GraphQL pleinement fonctionnel, mais, selon l'approche choisie, nous pourrons accomplir plus ou moins de fonctionnalités, plus ou moins facilement, sur le long terme. Ces deux approches sont appelées, respectivement, « schema-first » (mieux appelée « SDL-first ») et « code-first ».
Gato GraphQL utilise l'approche code-first. Voyons pourquoi.
Pourquoi Gato GraphQL utilise code-first
Dans l'approche code-first, nous commençons par coder les resolvers, puis, à partir du code comme source unique de vérité, nous générons le schéma comme un artefact. Ainsi, le schéma est créé en exécutant un script, plutôt qu'en le créant manuellement comme dans SDL-first. Puisque code-first dispose également d'un schéma, il ne manque rien de significatif par rapport à ce qu'offre SDL-first.
Cependant, code-first apporte une caractéristique importante par rapport à SDL-first : la possibilité de fournir des schémas dynamiques, qui peuvent changer de forme et d'attributs en fonction du contexte, et régulés par le code à l'exécution. De fait, toutes les grandes fonctionnalités offertes par Gato GraphQL sont une conséquence directe de son adoption de code-first.
Avantages de code-first
Un schéma dynamique apporte, entre autres, tous les avantages listés ci-dessous :
La source de vérité du schéma est un sur-ensemble de ce qu'exige GraphQL. Les propriétés supplémentaires (telles que les champs globaux, les connexions globales, les directives globales et les fragments persistés) peuvent déjà être utilisées dans notre API sans attendre qu'elles soient ajoutées à la spécification GraphQL, si tant est qu'elles le soient un jour.
Comme la source de vérité n'est pas liée au schéma, nous pouvons également générer n'importe quel schéma pour n'importe quel autre système : GraphQL n'est qu'une des cibles possibles. Par exemple, il peut générer un JSON-schema pour un service REST à partir de la même source de vérité.
L'API peut être publique/privée en même temps, selon que l'utilisateur est connecté ou non et selon les rôles de l'utilisateur connecté, ou offrir plus ou moins de champs en fonction d'une autre propriété, comme le fait que l'utilisateur a souscrit à l'abonnement PRO.
Les types ne savent pas à l'avance quels champs ils résoudront. En revanche, les resolvers de champ s'associent aux resolvers de type via le patron publish-subscribe, et les resolvers de champ peuvent en remplacer d'autres. Cette caractéristique rend l'API très extensible, nous permettant de disposer d'un code général pour notre API et de le personnaliser au niveau de l'application pour un client ou un projet spécifique.
Un champ peut être traité non pas par un seul resolver, mais par plusieurs : chaque resolver de champ dans la chaîne peut décider, à l'exécution, de traiter le champ ou non en fonction d'une propriété, ou de le passer à la chaîne suivante. Par exemple, un resolver de champ spécial peut n'être utilisé que si un argument de champ "source: testing" est passé, permettant de le tester sur quelques sites en production avant la mise en ligne générale ; la même stratégie permet également de fournir des correctifs rapides pour un client ou un environnement spécifique sans risquer de provoquer des effets de bord non souhaités ailleurs.
Les types et les interfaces peuvent être automatiquement placés dans des espaces de noms pour éviter les collisions avec des tiers.