Let's say I have a query defined liked this:
type Query {
# The basic me query
getUser(id:Int): [User]
}
type User {
id: ID!
login: String!
name: String
}
But now I need to have a mutation to add a user. My intuition would be to to something like this:
type Mutation {
addUser(newUser: User): [User]
}
But it is not working because a mutation cannot use "query type". It need to use "input type". I know that in this example, it is not really complicated to do, but if user was a really complex type with many sub-type used. How can I do that? Is there a was to re-use a type as a mutation argument?
Unfortunately, a type
cannot be used in place of an input
, and an input
cannot be used in place of a type
. This is by design. From the official specification:
Fields can define arguments that the client passes up with the query, to configure their behavior. These inputs can be Strings or Enums, but they sometimes need to be more complex than this.
The Object type defined above is inappropriate for re‐use here, because Objects can contain fields that express circular references or references to interfaces and unions, neither of which is appropriate for use as an input argument. For this reason, input objects have a separate type in the system.
What's more, fields on a GraphQLObjectType
can have args and a resolve function, while those on an GraphQLInputObjectType
do not (but they do have default values, which are not available to the former).
It also makes sense to keep them separate from an implementation standpoint. Simple schemas are likely to just map fields to columns in some table. However, in real-world applications, it's much more likely that you'll have derived fields that don't map to any one column (and would not be appropriate to use inside an input).
It's also likely you'll only want some fields to be used as input (if you're adding a user, for example, a client shouldn't send you an id; this should probably be generated by the db when the user is added). Likewise, you may not want to expose every field that's used as input to the client, only those they actually need.
If nothing else, your use of non-null
will probably be different between an input and a returned type.
That said, there is something of a workaround. In graphql-js, at least. If you declare your schema programatically, you could separately define an Object with your set of fields, and then set your fields
property for both your User
and UserInput
objects. Alternatively, if you're defining your schema declaratively (like in your example), you could use template literals like this:
const userFields = `
id: ID!
login: String!
name: String
`
const schema = `
type User {
${userFields}
}
type UserInput {
${userFields}
}
`
Heck, if you wanted to, you could even iterate over every defined type and programatically create a matching input type. However, IMO, the effort in implementing any of these workarounds is probably not worth it when you consider the cost in flexibility. Bite the bullet, put some thought into what you actually need as an input to that mutation and specify only the fields you need.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With