Skip to content

Mutations ​

Mutations in GQLB allow you to create, update, and delete data with full type safety. This page shows how to build various types of mutations.

Basic Mutations ​

Creating Data ​

typescript
import { b } from './generated'

const CREATE_USER_MUTATION = b.mutation('CreateUser', {
  input: 'CreateUserInput!'
}, (b, v) => [
  b.createUser({ input: v.input }, b => [
    b.user(b => [
      b.id(),
      b.name(),
      b.email(),
      b.createdAt(),
    ]),
    b.errors(b => [
      b.field(),
      b.message(),
    ])
  ])
])

Generated GraphQL:

graphql
mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    __typename
    user {
      __typename
      id
      name
      email
      createdAt
    }
    errors {
      __typename
      field
      message
    }
  }
}

Updating Data ​

typescript
const UPDATE_USER_MUTATION = b.mutation('UpdateUser', {
  id: 'ID!',
  input: 'UpdateUserInput!'
}, (b, v) => [
  b.updateUser({ 
    id: v.id, 
    input: v.input 
  }, b => [
    b.user(b => [
      b.id(),
      b.name(),
      b.email(),
      b.updatedAt(),
    ]),
    b.success(),
    b.errors(b => [
      b.field(),
      b.message(),
    ])
  ])
])

Deleting Data ​

typescript
const DELETE_USER_MUTATION = b.mutation('DeleteUser', {
  id: 'ID!'
}, (b, v) => [
  b.deleteUser({ id: v.id }, b => [
    b.success(),
    b.deletedId(),
    b.errors(b => [
      b.message(),
    ])
  ])
])

Complex Input Objects ​

Handle nested input objects and arrays:

typescript
const CREATE_POST_MUTATION = b.mutation('CreatePost', {
  input: 'CreatePostInput!'
}, (b, v) => [
  b.createPost({ input: v.input }, b => [
    b.post(b => [
      b.id(),
      b.title(),
      b.content(),
      b.slug(),
      b.status(),
      b.publishedAt(),
      
      // Author information
      b.author(b => [
        b.id(),
        b.name(),
      ]),
      
      // Tags (array of objects)
      b.tags(b => [
        b.id(),
        b.name(),
        b.slug(),
      ]),
      
      // Categories
      b.categories(b => [
        b.id(),
        b.name(),
      ])
    ]),
    b.errors(b => [
      b.field(),
      b.message(),
      b.code(),
    ])
  ])
])

// Usage with complex input
const postInput = {
  title: "My New Blog Post",
  content: "This is the content of my blog post...",
  status: "DRAFT",
  tags: [
    { name: "TypeScript" },
    { name: "GraphQL" }
  ],
  categoryIds: ["cat-1", "cat-2"],
  metadata: {
    excerpt: "A brief excerpt...",
    readTime: 5,
    featuredImage: "https://example.com/image.jpg"
  }
}

File Upload Mutations ​

Handle file uploads with proper type safety:

typescript
const UPLOAD_AVATAR_MUTATION = b.mutation('UploadAvatar', {
  file: 'Upload!',
  userId: 'ID!'
}, (b, v) => [
  b.uploadAvatar({ 
    file: v.file, 
    userId: v.userId 
  }, b => [
    b.user(b => [
      b.id(),
      b.avatar(),
      b.updatedAt(),
    ]),
    b.uploadInfo(b => [
      b.filename(),
      b.mimetype(),
      b.size(),
      b.url(),
    ]),
    b.success(),
    b.errors(b => [
      b.field(),
      b.message(),
    ])
  ])
])

const UPLOAD_MULTIPLE_FILES = b.mutation('UploadFiles', {
  files: '[Upload!]!',
  postId: 'ID!'
}, (b, v) => [
  b.uploadFiles({ 
    files: v.files, 
    postId: v.postId 
  }, b => [
    b.post(b => [
      b.id(),
      b.attachments(b => [
        b.id(),
        b.filename(),
        b.url(),
        b.mimetype(),
        b.size(),
      ])
    ]),
    b.uploadedCount(),
    b.errors(b => [
      b.filename(),
      b.message(),
    ])
  ])
])

Batch Mutations ​

Perform multiple operations in a single mutation:

typescript
const BATCH_UPDATE_POSTS = b.mutation('BatchUpdatePosts', {
  updates: '[PostUpdateInput!]!'
}, (b, v) => [
  b.batchUpdatePosts({ updates: v.updates }, b => [
    b.posts(b => [
      b.id(),
      b.title(),
      b.status(),
      b.updatedAt(),
    ]),
    b.successCount(),
    b.failureCount(),
    b.errors(b => [
      b.postId(),
      b.field(),
      b.message(),
    ])
  ])
])

const BULK_DELETE_POSTS = b.mutation('BulkDeletePosts', {
  ids: '[ID!]!'
}, (b, v) => [
  b.bulkDeletePosts({ ids: v.ids }, b => [
    b.deletedIds(),
    b.deletedCount(),
    b.errors(b => [
      b.postId(),
      b.message(),
    ])
  ])
])

Conditional Mutations ​

Use variables to conditionally execute different mutation logic:

typescript
const FLEXIBLE_POST_MUTATION = b.mutation('FlexiblePostMutation', {
  postId: 'ID',
  createInput: 'CreatePostInput',
  updateInput: 'UpdatePostInput',
  action: 'String!'
}, (b, v) => [
  // Conditional create
  ...(b.if(v.action === 'CREATE' && v.createInput, [
    b.createPost({ input: v.createInput }, b => [
      b.post(b => [
        b.id(),
        b.title(),
        b.status(),
      ]),
      b.errors(b => [
        b.field(),
        b.message(),
      ])
    ], 'createResult')
  ])),
  
  // Conditional update
  ...(b.if(v.action === 'UPDATE' && v.postId && v.updateInput, [
    b.updatePost({ 
      id: v.postId, 
      input: v.updateInput 
    }, b => [
      b.post(b => [
        b.id(),
        b.title(),
        b.status(),
        b.updatedAt(),
      ]),
      b.errors(b => [
        b.field(),
        b.message(),
      ])
    ], 'updateResult')
  ])),
  
  // Conditional delete
  ...(b.if(v.action === 'DELETE' && v.postId, [
    b.deletePost({ id: v.postId }, b => [
      b.success(),
      b.deletedId(),
      b.errors(b => [
        b.message(),
      ])
    ], 'deleteResult')
  ]))
])

Optimistic Updates ​

Structure mutations for optimistic UI updates:

typescript
const LIKE_POST_MUTATION = b.mutation('LikePost', {
  postId: 'ID!'
}, (b, v) => [
  b.likePost({ postId: v.postId }, b => [
    b.post(b => [
      b.id(),
      b.likesCount(),
      b.isLikedByCurrentUser(),
    ]),
    b.success(),
    b.errors(b => [
      b.message(),
    ])
  ])
])

const FOLLOW_USER_MUTATION = b.mutation('FollowUser', {
  userId: 'ID!'
}, (b, v) => [
  b.followUser({ userId: v.userId }, b => [
    b.user(b => [
      b.id(),
      b.followersCount(),
      b.isFollowedByCurrentUser(),
    ]),
    b.currentUser(b => [
      b.id(),
      b.followingCount(),
    ]),
    b.success(),
    b.errors(b => [
      b.message(),
    ])
  ])
])

Nested Object Creation ​

Create complex nested structures in a single mutation:

typescript
const CREATE_BLOG_WITH_POSTS = b.mutation('CreateBlogWithPosts', {
  blogInput: 'CreateBlogInput!',
  postsInput: '[CreatePostInput!]!'
}, (b, v) => [
  b.createBlogWithPosts({ 
    blog: v.blogInput,
    posts: v.postsInput 
  }, b => [
    b.blog(b => [
      b.id(),
      b.name(),
      b.slug(),
      b.description(),
      
      // Created posts
      b.posts(b => [
        b.id(),
        b.title(),
        b.slug(),
        b.status(),
      ]),
      
      // Blog owner
      b.owner(b => [
        b.id(),
        b.name(),
      ])
    ]),
    b.createdPostsCount(),
    b.errors(b => [
      b.field(),
      b.message(),
      b.path(),
    ])
  ])
])

Error Handling Patterns ​

Handle different types of mutation errors:

typescript
const COMPREHENSIVE_UPDATE = b.mutation('ComprehensiveUpdate', {
  userId: 'ID!',
  profileInput: 'UpdateProfileInput!',
  settingsInput: 'UpdateSettingsInput!'
}, (b, v) => [
  b.updateUserProfile({ 
    userId: v.userId,
    profile: v.profileInput,
    settings: v.settingsInput 
  }, b => [
    // Success data
    b.user(b => [
      b.id(),
      b.name(),
      b.email(),
      b.profile(b => [
        b.bio(),
        b.website(),
        b.avatar(),
      ]),
      b.settings(b => [
        b.theme(),
        b.notifications(),
        b.privacy(),
      ])
    ]),
    
    // Operation status
    b.success(),
    b.partialSuccess(),
    
    // Different error types
    b.validationErrors(b => [
      b.field(),
      b.message(),
      b.code(),
    ]),
    
    b.businessErrors(b => [
      b.type(),
      b.message(),
      b.details(),
    ]),
    
    b.systemErrors(b => [
      b.message(),
      b.retryable(),
    ])
  ])
])

// Type extraction for error handling
type UpdateResult = OutputOf<typeof COMPREHENSIVE_UPDATE>

function handleUpdateResult(result: UpdateResult['updateUserProfile']) {
  if (result.success) {
    console.log('Update successful!')
    console.log(`Updated user: ${result.user?.name}`)
  } else if (result.partialSuccess) {
    console.log('Partial success - some updates failed')
  }
  
  if (result.validationErrors.length > 0) {
    console.log('Validation errors:')
    result.validationErrors.forEach(error => {
      console.log(`- ${error.field}: ${error.message}`)
    })
  }
  
  if (result.businessErrors.length > 0) {
    console.log('Business logic errors:')
    result.businessErrors.forEach(error => {
      console.log(`- ${error.type}: ${error.message}`)
    })
  }
}

Real-time Mutations ​

Mutations that trigger real-time updates:

typescript
const SEND_MESSAGE_MUTATION = b.mutation('SendMessage', {
  channelId: 'ID!',
  content: 'String!',
  attachments: '[MessageAttachmentInput!]'
}, (b, v) => [
  b.sendMessage({ 
    channelId: v.channelId,
    content: v.content,
    attachments: v.attachments 
  }, b => [
    b.message(b => [
      b.id(),
      b.content(),
      b.createdAt(),
      b.updatedAt(),
      
      // Author
      b.author(b => [
        b.id(),
        b.name(),
        b.avatar(),
      ]),
      
      // Channel
      b.channel(b => [
        b.id(),
        b.name(),
      ]),
      
      // Attachments
      b.attachments(b => [
        b.id(),
        b.type(),
        b.url(),
        b.filename(),
      ])
    ]),
    
    // Real-time notification info
    b.notificationsSent(),
    b.recipientIds(),
    
    b.success(),
    b.errors(b => [
      b.field(),
      b.message(),
    ])
  ])
])

Type Extraction for Mutations ​

Extract and use mutation result types:

typescript
import { OutputOf, VariablesOf } from './generated'

// Extract variable types
type CreateUserVariables = VariablesOf<typeof CREATE_USER_MUTATION>
type UpdateUserVariables = VariablesOf<typeof UPDATE_USER_MUTATION>

// Extract result types
type CreateUserResult = OutputOf<typeof CREATE_USER_MUTATION>
type UpdateUserResult = OutputOf<typeof UPDATE_USER_MUTATION>

// Use in functions
async function createUser(
  variables: CreateUserVariables
): Promise<CreateUserResult['createUser']> {
  const result = await executeOperation(CREATE_USER_MUTATION, variables)
  return result.createUser
}

function handleUserCreation(result: CreateUserResult['createUser']) {
  if (result.user) {
    console.log(`Created user: ${result.user.name} (${result.user.id})`)
  }
  
  if (result.errors.length > 0) {
    console.log('Creation errors:')
    result.errors.forEach(error => {
      console.log(`- ${error.field}: ${error.message}`)
    })
  }
}

Mutation Composition ​

Combine multiple mutations for complex operations:

typescript
// Individual mutations
const CREATE_POST_MUTATION = b.mutation('CreatePost', {
  input: 'CreatePostInput!'
}, (b, v) => [
  b.createPost({ input: v.input }, b => [
    b.post(b => [b.id(), b.title()]),
    b.errors(b => [b.field(), b.message()])
  ])
])

const ADD_TAGS_MUTATION = b.mutation('AddTags', {
  postId: 'ID!',
  tagIds: '[ID!]!'
}, (b, v) => [
  b.addTagsToPost({ postId: v.postId, tagIds: v.tagIds }, b => [
    b.post(b => [
      b.id(),
      b.tags(b => [b.id(), b.name()])
    ]),
    b.success()
  ])
])

const PUBLISH_POST_MUTATION = b.mutation('PublishPost', {
  postId: 'ID!'
}, (b, v) => [
  b.publishPost({ postId: v.postId }, b => [
    b.post(b => [
      b.id(),
      b.status(),
      b.publishedAt()
    ]),
    b.success()
  ])
])

// Composite operation
async function createAndPublishPost(
  postData: CreatePostInput,
  tagIds: string[]
) {
  // 1. Create the post
  const createResult = await executeOperation(CREATE_POST_MUTATION, {
    input: postData
  })
  
  if (!createResult.createPost.post) {
    throw new Error('Failed to create post')
  }
  
  const postId = createResult.createPost.post.id
  
  // 2. Add tags
  await executeOperation(ADD_TAGS_MUTATION, {
    postId,
    tagIds
  })
  
  // 3. Publish
  const publishResult = await executeOperation(PUBLISH_POST_MUTATION, {
    postId
  })
  
  return publishResult.publishPost.post
}

Next Steps ​

Released under the MIT License.