web-dev-qa-db-fra.com

Type de champ Apollo / GraphQL pour objet avec clés dynamiques

Supposons que mon serveur graphql veuille récupérer les données suivantes au format JSON où person3 et person5 sont des identifiants:

"persons": {
  "person3": {
    "id": "person3",
    "name": "Mike"
  },
  "person5": {
    "id": "person5",
    "name": "Lisa"
  }
}

Question: Comment créer la définition de type de schéma avec apollo?

Les clés person3 et person5 ici sont générés dynamiquement en fonction de ma requête (c'est-à-dire le area utilisé dans la requête). Donc à un autre moment, je pourrais avoir person1, person2, person3 revenu. Comme vous le voyez, persons n'est pas un Iterable, donc ce qui suit ne fonctionnera pas comme une définition de type graphql que j'ai faite avec apollo:

type Person {
  id: String
  name: String
}
type Query {
  persons(area: String): [Person]
}

Les clés de l'objet persons peuvent toujours être différentes.

Une solution serait bien sûr de transformer les données JSON entrantes pour utiliser un tableau pour persons, mais n'y a-t-il aucun moyen de travailler avec les données en tant que telles?

18
Andru

GraphQL repose à la fois sur le serveur et sur le client sachant à l'avance quels champs sont disponibles disponibles pour chaque type. Dans certains cas, le client peut découvrir ces champs (via l'introspection), mais pour le serveur, ils doivent toujours être connus à l'avance. Il n'est donc pas vraiment possible de générer dynamiquement ces champs en fonction des données renvoyées.

Vous pourriez utiliser un scalaire JSON personnalisé (module graphql-type-json) et le renvoyer pour votre requête:

type Query {
  persons(area: String): JSON
}

En utilisant JSON, vous contournez l'exigence que les données retournées correspondent à n'importe quelle structure spécifique, de sorte que vous pouvez renvoyer ce que vous voulez tant qu'il est correctement formaté JSON.

Bien sûr, cela présente des inconvénients importants. Par exemple, vous perdez le filet de sécurité fourni par le (s) type (s) que vous auriez précédemment utilisé (littéralement n'importe quelle structure pourrait être retournée, et si vous retournez la mauvaise, vous ne le découvrirez pas tant que le client n'aura pas essayé pour l'utiliser et échoue). Vous perdez également la possibilité d'utiliser des résolveurs pour tous les champs des données renvoyées.

Mais ... vos funérailles :)

En passant, j'envisagerais d'aplatir les données dans un tableau (comme vous l'avez suggéré dans votre question) avant de les renvoyer au client. Si vous écrivez le code client et travaillez avec une liste de clients de taille dynamique, il est probable qu'un tableau sera beaucoup plus facile à utiliser qu'avec un objet indexé par id. Si vous utilisez React, par exemple, et affichez un composant pour chaque client, vous finirez par convertir cet objet en un tableau pour le mapper de toute façon. Lors de la conception de votre API, je ferais de l'utilisabilité du client une considération plus importante que d'éviter un traitement supplémentaire de vos données.

17
Daniel Rearden

Vous pouvez écrire votre propre GraphQLScalarType et décrire précisément votre objet et vos clés dynamiques, ce que vous autorisez et ce que vous n'autorisez pas ou ne transformez pas.

Voir https://graphql.org/graphql-js/type/#graphqlscalartype

Vous pouvez voir taion/graphql-type-json où il crée un scalaire qui autorise et transforme tout type de contenu:

https://github.com/taion/graphql-type-json/blob/master/src/index.js

1
Lukas

J'ai eu un problème similaire avec les clés dynamiques dans un schéma et j'ai fini par choisir une solution comme celle-ci:

query lookupPersons {
  persons {
    personKeys
    person3: personValue(key: "person1") {
      id
      name
    }
  }
}

retour:

{
  data: {
    persons: {
      personKeys: ["person1", "person2", "person3"]
      person3: {
        id: "person3"
        name: "Mike"
      }
    }
  }
}

en déplaçant la complexité vers la requête, il simplifie la forme de la réponse. l'avantage par rapport à l'approche JSON est qu'il n'a pas besoin de désérialisation du client

0
teebszet