Ressources
RessourcesBonnes pratiques GraphQL

Bonnes pratiques GraphQL

GraphQL est suffisamment mature, et est présent depuis assez longtemps, pour que la communauté ait publié de nombreux articles partageant des bonnes pratiques. Ces guides couvrent pratiquement tous les aspects de GraphQL, notamment la conception du schéma, les conventions de nommage, la gestion de la sécurité et la fourniture d'erreurs significatives, entre autres.

Voici quelques-uns des guides les plus convaincants sur les bonnes pratiques en GraphQL.

Bonnes pratiques sur graphql.org

Le site officiel de GraphQL propose une introduction générale aux bonnes pratiques en GraphQL.

Ces points couvrent principalement des préoccupations de haut niveau, telles que :

Où la couche GraphQL est placée au sein de l'architecture

Les recommandations de Lee Byron

Peu après avoir présenté GraphQL au monde, le créateur de GraphQL Lee Byron a publié l'article Lessons From 4 Years of GraphQL, décrivant comment nous devrions conceptuellement tenter de travailler avec GraphQL :

  • Le nommage est important
  • Pensez en graphes, pas en endpoints
  • DĂ©crivez les donnĂ©es, pas la vue
  • GraphQL est une interface lĂ©gère
  • Cachez les dĂ©tails d'implĂ©mentation

Il détaille également plusieurs principes et leçons précieux qu'il a appris en utilisant GraphQL chez Facebook.

GraphQL Rules

GraphQL Rules est un site spécialement dédié à présenter les bonnes pratiques quotidiennes pour travailler avec GraphQL, concernant principalement la conception du schéma GraphQL.

Cette ressource est très complète. Elle compile les informations de quelques ressources exceptionnelles (comme l'article Designing GraphQL Mutations et le tutoriel de Shopify Designing a GraphQL API) et les présente toutes ensemble de manière concise.

Les règles décrites incluent :

  1. Règles de nommage
    • Utilisez camelCase pour les champs et arguments GraphQL.
    • Utilisez UpperCamelCase pour les types GraphQL.
    • Utilisez CAPITALIZED_WITH_UNDERSCORES pour nommer les types ENUM.
  2. Règles de types
    • Utilisez des types scalaires personnalisĂ©s si vous souhaitez dĂ©clarer des champs ou des arguments avec une valeur sĂ©mantique spĂ©cifique.
    • Utilisez Enum pour les champs qui contiennent un ensemble spĂ©cifique de valeurs.
  3. Règles de champs (Output)
    • Utilisez des noms sĂ©mantiques pour les champs et Ă©vitez de laisser transparaĂ®tre les dĂ©tails d'implĂ©mentation dans les noms des champs.
    • Utilisez le champ Non-Null si le champ aura toujours une valeur donnĂ©e.
    • Regroupez autant de champs liĂ©s que possible dans un Object type personnalisĂ©.
  4. Règles d'arguments (Input)
    • Regroupez les arguments couplĂ©s dans un nouveau input-type.
    • Utilisez des types scalaires stricts pour les arguments, par ex. DateTime au lieu de String.
    • Marquez les arguments comme required s'ils sont nĂ©cessaires Ă  l'exĂ©cution de la requĂŞte.
  5. Règles des listes
    • Pour filtrer les listes, utilisez l'argument filter, qui contient tous les filtres disponibles.
    • Utilisez l'argument sort de type Enum ou [Enum!] pour le tri des listes.
    • Utilisez limit avec une valeur par dĂ©faut et skip pour limiter le nombre d'Ă©lĂ©ments retournĂ©s dans la liste.
    • Utilisez les arguments page, perPage pour la pagination et retournez un output type avec items (tableau d'Ă©lĂ©ments) et pageInfo (mĂ©ta-donnĂ©es).
    • Pour les listes infinies (dĂ©filement infini), utilisez la Relay Cursor Connections Specification.
  6. Règles des mutations
    • Utilisez les Namespace-types pour regrouper les mutations au sein d'une seule ressource.
    • Allez au-delĂ  du CRUD – crĂ©ez de petites mutations pour diffĂ©rentes opĂ©rations mĂ©tier sur les ressources.
    • Envisagez la possibilitĂ© d'effectuer des mutations sur plusieurs Ă©lĂ©ments (modifications par lot du mĂŞme type).
    • Les mutations doivent dĂ©crire clairement tous les arguments obligatoires, il ne doit pas y avoir d'options either-either.
    • Dans les mutations, placez toutes les variables dans un unique argument input.
    • Chaque mutation doit avoir un payload type unique.

Bonnes pratiques pour les resolvers

L'article GraphQL Resolvers: Best Practices décrit comment créer au mieux les resolvers de champs. Bien qu'il soit destiné aux serveurs Node.js (l'infrastructure de PayPal est basée sur Express), plusieurs de ses leçons peuvent également s'appliquer à d'autres technologies, y compris PHP.

Les principaux enseignements sont :

  • La rĂ©cupĂ©ration et le passage de donnĂ©es de parent Ă  enfant doit ĂŞtre utilisĂ© avec modĂ©ration.
  • Utilisez des bibliothèques comme dataloader pour dĂ©dupliquer les requĂŞtes descendantes.
  • Soyez conscient de la pression que vous exercez sur vos sources de donnĂ©es.
  • Ne mutez pas « context ». Garantit un code cohĂ©rent et moins sujet aux bugs.
  • Écrivez des resolvers lisibles, maintenables et testables. Pas trop complexes.
  • Rendez vos resolvers aussi lĂ©gers que possible. Extrayez la logique de rĂ©cupĂ©ration de donnĂ©es vers des fonctions async rĂ©utilisables.

OWASP - GraphQL Cheat Sheet

L'OWASP (Open Web Application Security Project) est une fondation à but non lucratif qui œuvre pour améliorer la sécurité des logiciels. Elle mène des recherches sur la manière dont différentes technologies sont vulnérables aux exploits, et décrit en détail des solutions aux problèmes de sécurité, facilitant ainsi la prévention des attaques pour les développeurs.

L'OWASP a publié la GraphQL Cheat Sheet, expliquant quelles sont les attaques les plus courantes et les plus grands problèmes de sécurité dans GraphQL, et comment les traiter.

Les attaques courantes contre GraphQL sont :

  1. Injection - cela inclut généralement mais ne se limite pas à :
    • Injection SQL et NoSQL
    • Injection de commandes OS
    • Injection SSRF et CRLF / Request Smuggling
  2. DoS (Denial of Service)
  3. Abus d'autorisation défaillante : accès impropre ou excessif, incluant IDOR
  4. Batching Attacks, une méthode d'attaque par force brute spécifique à GraphQL
  5. Abus de configurations par défaut non sécurisées

L'OWASP fournit ensuite des recommandations sur la façon d'éviter chacune de ces situations.

Bonnes pratiques avec les requĂŞtes GraphQL

L'équipe Apollo a publié les GraphQL query best practices, donnant des perspectives pratiques sur les façons concrètes de composer la requête GraphQL.

Par exemple, ces deux requêtes atteignent le même objectif, mais parce que la première a un nom d'opération, elle est plus compréhensible et utile lors du débogage :

# Recommandé ✅
query GetBooks {
  books {
    title
  }
}
 
# Non recommandé ❌
query {
  books {
    title
  }
}

Leurs suggestions incluent :

  • Nommer toutes les opĂ©rations
  • Utiliser des variables pour fournir les arguments GraphQL
  • Interroger uniquement les donnĂ©es dont vous avez besoin, lĂ  oĂą vous en avez besoin
  • Utiliser des fragments pour encapsuler des ensembles de champs liĂ©s
  • Interroger les donnĂ©es globales et les donnĂ©es spĂ©cifiques Ă  l'utilisateur sĂ©parĂ©ment

Tirer parti du one graph

Également de l'équipe Apollo, le site Principled GraphQL explique que GraphQL n'est pas seulement une spécification mais, peut-être plus important encore, une interface pour interagir avec le « graphe » de données de notre entreprise.

À travers une liste de 10 principes, ce site décrit comment tirer le meilleur parti du graphe unique :

  1. One Graph : votre entreprise doit disposer d'un graphe unifié, plutôt que de multiples graphes créés par chaque équipe.
  2. Federated Implementation : bien qu'il n'y ait qu'un seul graphe, l'implémentation de ce graphe doit être fédérée entre plusieurs équipes.
  3. Track the Schema in a Registry : il doit y avoir une source unique de vérité pour enregistrer et suivre le graphe.
  4. Abstract, Demand-Oriented Schema : le schéma doit agir comme une couche d'abstraction qui offre de la flexibilité aux consommateurs tout en cachant les détails d'implémentation du service.
  5. Use an Agile Approach to Schema Development : le schéma doit être construit de manière incrémentale sur la base d'exigences réelles et évoluer progressivement au fil du temps.
  6. Iteratively Improve Performance : la gestion des performances doit être un processus continu, basé sur les données, s'adaptant progressivement aux charges de requêtes et aux implémentations de services changeantes.
  7. Use Graph Metadata to Empower Developers : les développeurs doivent être équipés d'une connaissance approfondie du graphe tout au long du processus de développement.
  8. Access and Demand Control : accordez l'accès au graphe par client, et gérez ce que les clients peuvent accéder et comment.
  9. Structured Logging : capturez des logs structurés de toutes les opérations du graphe et utilisez-les comme outil principal pour comprendre l'utilisation du graphe.
  10. Separate the GraphQL Layer from the Service Layer : adoptez une architecture en couches avec la fonctionnalité du graphe divisée en une couche séparée plutôt qu'intégrée dans chaque service.