The input type in a GraphQL schema is a special object type that groups a set of arguments together, and can then be used as an argument to another field. Using input types helps us group and understand arguments, especially for mutations.
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).
Strongly-typed: GraphQL is strongly-typed. Given a query, tooling can ensure that the query is both syntactically correct and valid within the GraphQL type system before execution, i.e. at development time, and the server can make certain guarantees about the shape and nature of the response.
There are three types of operations that GraphQL models: query – a read‐only fetch. mutation – a write followed by a fetch. subscription – a long‐lived request that fetches data in response to source events.
From the spec:
The GraphQL Object type (ObjectTypeDefinition)... is inappropriate for re‐use [as an input], because Object types can contain fields that define arguments or contain 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.
That's the "official reason", but there's several practical reasons why you can't use an object type as an input object type or use an object type as an input object type:
Object types and input object types both have fields, however those fields have different properties that reflect how these types are used by the schema. Your schema will potentially define arguments and some kind of resolver function for an object type's fields, but these properties don't make sense in an input context (i.e. you can't resolve an input object's field -- it already has an explicit value). Similarly, default values can only be provided for input object type fields, and not object type fields.
In other words, this may seem like duplication:
type Student {
name: String
grade: Grade
}
input StudentInput {
name: String
grade: Grade
}
But adding features specific to either object types or input object types makes it clear that they behave differently:
type Student {
name(preferred: Boolean): String
grade: Grade
}
input StudentInput {
name: String
grade: Grade = F
}
Types in GraphQL are grouped into output types and input types.
Output types are types that may be returned as part of a response produced by a GraphQL service. Input types are types that are valid inputs for field or directive arguments.
There's overlap between these two groups (i.e. scalars, enums, lists and non-nulls). However, abstract types like unions and interfaces don't make sense in an input context and cannot be used as inputs. Separating object types and input object types allows you to ensure that an abstract type is never used where an input type is expected.
When representing an entity in your schema, it's likely that some entities will indeed "share fields" between their respective input and output types:
type Student {
firstName: String
lastName: String
grade: Grade
}
input StudentInput {
firstName: String
lastName: String
grade: Grade
}
However, object types can (and in reality frequently do) model very complex data structures:
type Student {
fullName: String!
classes: [Class!]!
address: Address!
emergencyContact: Contact
# etc
}
While these structures may translate into appropriate inputs (we create a Student, so we also pass in an object representing their address), often they do not -- i.e. maybe we need to specify the student's classes by class ID and section ID, not an object. Similarly, we may have fields that we want to return, but don't want to mutate, or vice versa (like a password
field).
Moreover, even for relatively simple entities, we often have different requirements around nullability between object types and their "counterpart" input objects. Often we want to guarantee that a field will also be returned in a response, but we don't want to make the same fields required in our input. For example,
type Student {
firstName: String!
lastName: String!
}
input StudentInput {
firstName: String
lastName: String
}
Lastly, in many schemas, there's often not a one-to-one mapping between object type and input object type for a given entity. A common pattern is to utilize separate input object types for different operations to further fine-tune the schema-level input validation:
input CreateUserInput {
firstName: String!
lastName: String!
email: String!
password: String!
}
input UpdateUserInput {
email: String
password: String
}
All of these examples illustrate an important point -- while an input object type may mirror an object type some of the time, you're much less likely to see that in production schemas due to business requirements.
Jesse's comment is correct. For more formal answer, here is the excerpt from GraphQL documentation on input types:
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.
Since posting it, I found that circular references actually are acceptable, so long as those are nilable (or else it would declare an infinite chain). But, still there are other limitations (e.g. interfaces) that seem to necessitate a separate type system for inputs.
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