Docs
Launch GraphOS Studio

Errors as Data explained

Use unions types to represent different response scenarios

schema-design

💡 TIP

If you're an enterprise customer looking for more material on this topic, try the Enterprise best practices: Schema design course on .

Not an enterprise customer? Learn about GraphOS for Enterprise.

Union types in are an excellent way to represent multiple types of data in a single . This can be extremely useful when dealing with different types of responses, such as success and error cases. By leveraging union types, developers can create a flexible and expressive API that handles different scenarios elegantly and efficiently.

One of the core features of GraphQL is its built-in error handling mechanism, which uses an errors array in the response to represent any errors that occurred during the request. However, not all errors may want to be included in this array. If we are following the Errors as Data pattern, the errors array should be used for system errors—those that would typically result in an HTTP 500 error.

System errors can include situations like:

  • Server crashes
  • Unhandled exceptions
  • Exhausted resources (for example, memory or CPU)

These errors are usually unexpected and can't be handled gracefully by the client. As a result, they should be logged and monitored on the server side, while the client should display a generic error message.

On the other hand, all other business logic errors, could be part of the known response types within the response union. This allows the client to handle these errors explicitly, providing better user experience and maintainable code.

In this example, we'll look at a checkout that processes a user's cart and creates an order. The possible responses for this mutation could include a successful order creation, insufficient stock, or an invalid payment method. We'll use union types to represent these different response scenarios.

union CheckoutResponse =
Order
| InsufficientStockError
| InvalidPaymentMethodError
type Mutation {
checkout(paymentMethod: ID!): CheckoutResponse
}
type Order {
id: ID!
items: [OrderItem!]!
totalPrice: Float!
status: String!
}
type OrderItem {
id: ID!
product: Product!
quantity: Int!
price: Float!
}
type Product {
id: ID!
name: String!
price: Float!
}
interface CheckoutError {
message: String!
}
type InsufficientStockError implements CheckoutError {
message: String!
product: Product!
availableStock: Int!
}
type InvalidPaymentMethodError implements CheckoutError {
message: String!
paymentMethod: ID!
}

In the schema above, the checkout mutation returns a CheckoutResponse union type that can be one of the following types: Order, InsufficientStockError, or InvalidPaymentMethodError. The client can handle each of these cases explicitly, ensuring a clear and expressive API design.

Now, let's look at a sample and the possible response handling:

mutation OrderCheckout($payment: ID!) {
checkout(paymentMethod: $payment) {
... on Order {
id
items {
product {
name
}
quantity
price
}
totalPrice
status
}
... on InsufficientStockError {
message
product {
name
}
availableStock
}
... on InvalidPaymentMethodError {
message
paymentMethod
}
}
}

In this mutation, the client requests different for each possible response type:

  1. For a successful order creation (Order), the client retrieves the order details, including the items, total price, and status.
  2. For an insufficient stock error (InsufficientStockError), the client retrieves the error message, the affected product, and the available stock information. This information can be used to display a user-friendly error message and suggest alternative actions, such as updating the cart.
  3. For an invalid payment method error (InvalidPaymentMethodError), the client retrieves the error message and the problematic payment method ID. This can be used to inform the user about the issue and prompt them to update their payment information.

By using the union type CheckoutResponse, the API provides a clean and flexible way to handle the different response scenarios that can occur during the checkout process. This allows the client to handle each case explicitly, provides strong typing for operation responses and enhances the developer experience.

Next
Home
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company