GraphQL Mutation Return Types — Designing Payloads for Create, Update, and Delete Operations
In this tutorial, you will learn about Graphql Mutation Return Types. We cover key concepts, practical examples, and best practices to help you master this topic.
GraphQL mutation return types define what data is returned after a mutation, typically including the modified object, status information, and user-visible errors for robust client handling.
What You'll Learn
- Designing mutation payload types
- Including errors in mutation responses
- Returning modified objects and status
Why It Matters
Well-designed mutation return types make client error handling reliable. Without structured mutation payloads, clients must parse error messages for common failure modes.
Code Examples
# Mutation payload types
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
}
type CreateUserPayload {
user: User
errors: [UserError!]
status: MutationStatus!
}
type UpdateUserPayload {
user: User
errors: [UserError!]
status: MutationStatus!
}
type DeleteUserPayload {
deletedId: ID
errors: [UserError!]
status: MutationStatus!
}
type UserError {
field: String!
message: String!
code: ErrorCode!
}
enum MutationStatus {
SUCCESS
PARTIAL_SUCCESS
VALIDATION_ERROR
NOT_FOUND
UNAUTHORIZED
ERROR
}
enum ErrorCode {
REQUIRED_FIELD
INVALID_FORMAT
DUPLICATE
NOT_FOUND
UNAUTHORIZED
INTERNAL_ERROR
}
// Mutation resolvers with structured responses
const resolvers = {
Mutation: {
createUser: async (parent, { input }, { db }) => {
const errors = [];
// Validation
if (!input.name) {
errors.push({ field: 'name', message: 'Name is required', code: 'REQUIRED_FIELD' });
}
if (input.email && !emailRegex.test(input.email)) {
errors.push({ field: 'email', message: 'Invalid email format', code: 'INVALID_FORMAT' });
}
if (errors.length > 0) {
return { user: null, errors, status: 'VALIDATION_ERROR' };
}
try {
const user = await db.users.create(input);
return { user, errors: [], status: 'SUCCESS' };
} catch (error) {
if (error.code === '23505') { // Unique violation
errors.push({ field: 'email', message: 'Email already exists', code: 'DUPLICATE' });
return { user: null, errors, status: 'VALIDATION_ERROR' };
}
return { user: null, errors: [], status: 'ERROR' };
}
}
}
};
# Python mutation payload
import strawberry
@strawberry.type
class UserError:
field: str
message: str
code: str
@strawberry.type
class CreateUserPayload:
user: User | None
errors: list[UserError] = []
status: MutationStatus = MutationStatus.SUCCESS
@strawberry.type
class Mutation:
@strawberry.mutation
async def create_user(self, input: CreateUserInput) -> CreateUserPayload:
errors = []
if not input.name:
errors.append(UserError(field='name', message='Required', code='REQUIRED'))
if errors:
return CreateUserPayload(user=None, errors=errors, status='VALIDATION_ERROR')
user = await create_user(input)
return CreateUserPayload(user=user)
Common Mistakes
1. Returning Only the Modified Object
Without status and errors, clients cannot distinguish validation errors from server errors.
2. Throwing Errors for Validation Failures
Use structured error payloads instead of throwing for expected validation failures.
3. Not Including Mutation Status
A status field allows clients to quickly determine the mutation outcome.
4. Exposing Internal Error Details
Do not leak stack traces or internal error messages in mutation payloads.
5. Returning Null for All Error Cases
Always return enough data for clients to handle errors gracefully.
Practice Questions
- Why use structured mutation payloads instead of throwing errors?
- What should a mutation payload include?
- How do you handle partial success in mutations?
- What error fields help clients handle failures?
- How do you design errors for batch mutations?
Answers:
- Structured payloads allow clients to handle validation errors without try-catch.
- The modified object, errors array, and status enum.
- Return the successfully modified items and errors for failed items.
- Field name, error message, and error code for programmatic handling.
- Return arrays of successful and failed items with error details per item.
Challenge: Design mutation payloads for a batch import operation that creates multiple users. Handle partial success where some users are created and others fail validation.
FAQ
Mini Project
Build a complete mutation system for a CRUD API with structured payloads for create, update, delete, and batch operations. Include validation errors, not-found errors, authorization errors, and success responses.
What's Next
Learn about subscription filtering for targeted event delivery, then explore subscription context for auth in subscriptions.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro