Lesson 10 / 25

Designing Mutations

Input and payload types.

Specific actions, single input, rich payload

Name mutations after business actions (placeOrder, cancelSubscription) rather than generic updateOrder with many optional fields. A common convention uses a single input argument and returns a payload type containing the changed objects (so clients can update their caches) and any user-facing errors. Make mutations idempotent where possible, for example by accepting a client-generated ID or idempotency key for operations such as payments.

Changing data and reporting problems

Well-designed mutations, clear error handling and real-time subscriptions complete the API.

Three ideas: mutation design, error handling, subscriptions.
Figure 4.1 — Mutations, errors and subscriptions.

A placeOrder mutation

Schema and a client operation.

input PlaceOrderInput {
  cartId: ID!
  shippingAddressId: ID!
  idempotencyKey: String!
}

type PlaceOrderPayload {
  order: Order
  userErrors: [UserError!]!
}

type UserError {
  field: [String!]
  message: String!
  code: String!
}

mutation Checkout($input: PlaceOrderInput!) {
  placeOrder(input: $input) {
    order { id status total { amount currency } }
    userErrors { field message code }
  }
}

Return what changed

Including the updated object with its id lets normalised client caches refresh automatically.

Quick check: Why return the modified object in a mutation payload?

  • So clients can update their caches without another request
  • Because GraphQL requires exactly one field
  • To slow down attackers
  • To avoid using variables
Answer

So clients can update their caches without another request — Caches normalise by id.