Designing Mutation Inputs
Good input design is one of the most important parts of a maintainable GraphQL API.
Instead of exposing many arguments:
createProduct(
name: String!
description: String
price: Float!
category: ProductCategory!
): Product!
prefer a structured input for complex operations:
input CreateProductInput {
name: String!
description: String
price: Float!
category: ProductCategory!
}
createProduct(input: CreateProductInput!): Product!
This approach creates a clear boundary around client-provided values.
For updates, consider the difference between fields that are omitted and fields explicitly set to null.
For example:
input UpdateUserInput {
name: String
bio: String
}
Depending on the application's semantics:
- Omitted
namemay mean "leave the name unchanged". name: nullmay mean "clear the name" if the schema permits null.
This distinction is important when designing partial updates.
For more complex systems, input objects can be nested:
input AddressInput {
street: String!
city: String!
postalCode: String!
}
input CreateCustomerInput {
name: String!
address: AddressInput!
}
Nested inputs allow domain concepts to remain explicit rather than flattening every property into a huge argument list.
Prefer input objects for complex operationsOften means no updateMay mean clear the value- Input objects create explicit boundaries for client-provided data.
- Complex mutations are usually easier to evolve with input objects.
- Omitted and null values can have different update semantics.
- Nested input types can model complex domain structures.
Why can distinguishing an omitted input field from an explicit null value matter in an update mutation?