Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apollo / GraphQl - Type must be Input type

Reaching to you all as I am in the learning process and integration of Apollo and graphQL into one of my projects. So far it goes ok but now I am trying to have some mutations and I am struggling with the Input type and Query type. I feel like it's way more complicated than it should be and therefore I am looking for advice on how I should manage my situation. Examples I found online are always with very basic Schemas but the reality is always more complex as my Schema is quite big and look as follow (I'll copy just a part):

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

}

Then Inputs and Loads are defined, and so on...

For this I want a mutation to save the "Calculation", so in the same file I have this:

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

My resolver is as follow:

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')
        }
    }
}

Then my mutation is the following : import gql from 'graphql-tag';

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

Finally I am using the Mutation component to try to save the data:

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

Now I get the following error :

[GraphQL error]: Message: The type of Mutation.saveCalculation(data:) must be Input Type but got: CalculationData!., Location: undefined, Path: undefined

From my research and some other SO posts, I get that I should define Input type in addition to the Query type but Input type can only avec Scalar types but my schema depends on other schemas (and that is not scalar). Can I create Input types depending on other Input types and so on when the last one has only scalar types? I am kinda lost cause it seems like a lot of redundancy. Would very much appreciate some guidance on the best practice. I am convinced Apollo/graphql could bring me quite good help over time on my project but I have to admit it is more complicated than I thought to implement it when the Schemas are a bit complex. Online examples generally stick to a String and a Boolean.

like image 473
Ivo Avatar asked Oct 10 '18 16:10

Ivo


People also ask

How do you define input type in GraphQL?

To define an input type, use the input keyword followed by the name and curly braces ( {} ). Inside the curly braces, we list the fields and types as usual. Note that fields of an input type can be only a scalar, an enum, or another input type. checkInDate: String!

Is it possible to use inheritance with GraphQL input types?

No, the spec does not allow input types to implement interfaces. And GraphQL type system in general does not define any form of inheritance (the extends keyword adds fields to an existing type, and isn't for inheritance).

What other types can be used in a GraphQL schema?

The GraphQL schema language supports the scalar types of String , Int , Float , Boolean , and ID , so you can use these directly in the schema you pass to buildSchema . By default, every type is nullable - it's legitimate to return null as any of the scalar types.

Is there an any type in GraphQL?

The GraphQL specification includes default scalar types Int , Float , String , Boolean , and ID . Although these scalars cover the majority of use cases, some applications need to support other atomic data types (such as Date ) or add validation to an existing type. To enable this, you can define custom scalar types.


2 Answers

From the spec:

Fields may accept arguments to configure their behavior. These inputs are often scalars or enums, but they sometimes need to represent more complex values.

A GraphQL Input Object defines a set of input fields; the input fields are either scalars, enums, or other input objects. This allows arguments to accept arbitrarily complex structs.

In other words, you can't use regular GraphQLObjectTypes as the type for an GraphQLInputObjectType field -- you must use another GraphQLInputObjectType.

When you write out your schema using SDL, it may seem redundant to have to create a Load type and a LoadInput input, especially if they have the same fields. However, under the hood, the types and inputs you define are turned into very different classes of object, each with different properties and methods. There is functionality that is specific to a GraphQLObjectType (like accepting arguments) that doesn't exist on an GraphQLInputObjectType -- and vice versa.

Trying to use in place of another is kind of like trying to put a square peg in a round hole. "I don't know why I need a circle. I have a square. They both have a diameter. Why do I need both?"

Outside of that, there's a good practical reason to keep types and inputs separate. That's because in plenty of scenarios, you will expose plenty of fields on the type that you won't expose on the input.

For example, your type might include derived fields that are actually a combination of the underlying data. Or it might include fields to relationships with other data (like a friends field on a User). In both these case, it wouldn't make sense to make these fields part of the data that's submitted as as argument for some field. Likewise, you might have some input field that you wouldn't want to expose on its type counterpart (a password field comes to mind).

like image 182
Daniel Rearden Avatar answered Oct 30 '22 05:10

Daniel Rearden


Yes, you can:

The fields on an input object type can themselves refer to input object types, but you can't mix input and output types in your schema. Input object types also can't have arguments on their fields.

Input types are meant to be defined in addition to normal types. Usually they'll have some differences, eg input won't have an id or createdAt field.

like image 33
Loren Avatar answered Oct 30 '22 05:10

Loren