web-dev-qa-db-fra.com

Apollo/GraphQl - Le type doit être le type d'entrée

Je souhaite vous parler à tous car je suis en train d’apprendre et d’intégrer Apollo et graphQL dans l’un de mes projets. Jusqu’à présent, tout va bien, mais j’essaie maintenant d’avoir des mutations et j’ai des difficultés avec le type d’entrée et le type de requête. Je pense que c'est beaucoup plus compliqué que cela ne devrait être et je cherche donc des conseils sur la façon dont je devrais gérer ma situation. Les exemples que j'ai trouvés en ligne sont toujours avec des schémas très basiques mais la réalité est toujours plus complexe car mon schéma est assez grand et ressemble à ce qui suit (je n'en copierai qu'une partie):

type Calculation {
    _id: String!
    userId: String!
    data: CalculationData
    lastUpdated: Int
    name: String
}

type CalculationData {
    Loads: [Load]
    validated: Boolean
    x: Float
    y: Float
    z: Float
    Inputs: [Input]
    metric: Boolean

}

Ensuite, les entrées et les charges sont définies, et ainsi de suite ...

Pour cela, je veux une mutation pour sauvegarder le "Calcul", donc dans le même fichier, j'ai ceci:

type Mutation {
    saveCalculation(data: CalculationData!, name: String!): Calculation
}

Mon résolveur est le suivant:

export default resolvers = {
    Mutation: {
        saveCalculation(obj, args, context) {
            if(context.user && context.user._id){
                const calculationId = Calculations.insert({
                    userId: context.user._id,
                    data: args.data,
                    name: args.name
                })
                return Calculations.findOne({ _id: calculationId})
            }
            throw new Error('Need an account to save a calculation')
        }
    }
}

Alors ma mutation est la suivante: importer le gql de 'graphql-tag';

export const SAVE_CALCULATION = gql`
    mutation saveCalculation($data: CalculationData!, $name: String!){
        saveCalculation(data: $data, name: $name){
            _id
        }
    }
`

Enfin, j'utilise le composant Mutation pour essayer de sauvegarder les données:

<Mutation mutation={SAVE_CALCULATION}>
    {(saveCalculation, { data }) => (
        <div onClick={() => saveCalculation({ variables : { data: this.state, name:'name calcul' }})}>SAVE</div>
    }}
</Mutation>

Maintenant, j'obtiens l'erreur suivante:

[Erreur GraphQL]: Message: Le type de Mutation.saveCalculation (data:) doit être de type entrée mais obtenu: CalculationData!., Emplacement: undefined, Chemin: non défini

D'après mes recherches et d'autres SO publications, je comprends que je devrais définir un type d'entrée en plus du type de requête, mais ce type ne peut contenir que des types scalaires, mais mon schéma dépend d'autres schémas (et ce n'est pas scalaire). Puis-je créer des types d'entrée en fonction d'autres types d'entrée et ainsi de suite lorsque le dernier en contient uniquement des types scalaires? Je suis un peu perdu parce qu'il semble y avoir beaucoup de redondance. J'apprécierais beaucoup des conseils sur les meilleures pratiques. Je suis convaincu Apollo/graphql que mon projet me serait très utile au fil du temps, mais je dois admettre que c’est plus compliqué que je ne le pensais de le mettre en œuvre lorsque les schémas sont un peu complexes. Les exemples en ligne s'en tiennent généralement à une chaîne et à un booléen.

6
Ivo

De la spec :

Les champs peuvent accepter des arguments pour configurer leur comportement. Ces entrées sont souvent des scalaires ou des énumérations, mais elles doivent parfois représenter des valeurs plus complexes.

Un objet d’entrée GraphQL définit un ensemble de champs d’entrée; les champs d'entrée sont des scalaires, des énumérations ou d'autres objets d'entrée. Cela permet aux arguments d'accepter des structures arbitrairement complexes.

En d'autres termes, vous ne pouvez pas utiliser GraphQLObjectTypes régulier comme type pour un champ GraphQLInputObjectType - vous devez utiliser un autre GraphQLInputObjectType.

Lorsque vous écrivez votre schéma à l'aide de SDL, il peut sembler redondant de devoir créer un type Load et une entrée LoadInput, en particulier s'ils ont les mêmes champs. Cependant, sous le capot, les types et les entrées que vous définissez sont transformés en classes d'objets très différentes, chacune avec des propriétés et des méthodes différentes. Il existe des fonctionnalités spécifiques à une GraphQLObjectType (comme l'acceptation des arguments) qui n'existent pas sur une GraphQLInputObjectType - et vice versa. 

Essayer d'utiliser à la place d'un autre, c'est un peu comme essayer de placer une cheville carrée dans un trou rond. "Je ne sais pas pourquoi j'ai besoin d'un cercle. J'ai un carré. Ils ont tous les deux un diamètre. Pourquoi ai-je besoin des deux?"

En dehors de cela, il existe une bonne raison pratique de séparer les types et les entrées. En effet, dans de nombreux scénarios, vous exposerez de nombreux champs sur le type que vous n'exposerez pas dans l'entrée.

Par exemple, votre type peut inclure des champs dérivés qui sont en réalité une combinaison des données sous-jacentes. Ou il peut inclure des champs pour des relations avec d'autres données (comme un champ friends sur un User). Dans ces deux cas, il serait insensé de faire de ces champs une partie des données soumises en tant qu'argument pour un champ. De même, vous pourriez avoir un champ d’entrée que vous ne voudriez pas exposer sur son équivalent de type (un champ password vient à l’esprit).

5
Daniel Rearden

Oui, vous pouvez:

Les champs d'un type d'objet d'entrée peuvent eux-mêmes faire référence à des types d'objet d'entrée, mais vous ne pouvez pas mélanger les types d'entrée et de sortie dans votre schéma. Les types d'objet d'entrée ne peuvent pas non plus avoir d'arguments sur leurs champs.

Les types d'entrée doivent être définis en plus des types normaux. Généralement, ils auront des différences, par exemple, les entrées n'auront pas d'identifiant ou de champ createdAt.

0
Loren