Skip to content

Mutations

All mutations are auto-generated by @GqlResolver. Each accepts typed inputs and returns the affected entity.

Create

mutation {
createPost(
data: { title: "Hello World", content: "My first post", published: false, authorId: 1 }
) {
id
title
content
published
createdAt
}
}

Input: Create{Entity}Input - all scalar fields except the primary key. Fields with defaults or nullable columns are optional, everything else is required.

Returns: The created entity.

Update

mutation {
updatePost(id: 1, data: { title: "Updated Title", published: true }) {
id
title
published
}
}

Input: Update{Entity}Input - same fields as create, but all optional. Only the provided fields are updated.

Returns: The updated entity. Throws NotFoundException if the entity does not exist.

Delete

mutation {
deletePost(id: 1) {
id
title
}
}

Returns: The deleted entity. Throws NotFoundException if the entity does not exist.

Disabling mutations

Disable any mutation by setting it to false:

@GqlResolver(PostEntity, {
mutations: {
deleteOne: false, // no deletePost mutation
},
})

Disable all mutations to create a read-only resolver:

@GqlResolver(PostEntity, {
mutations: {
createOne: false,
updateOne: false,
deleteOne: false,
},
})

Custom names

@GqlResolver(PostEntity, {
mutations: {
createOne: { name: 'addPost' },
updateOne: { name: 'editPost' },
deleteOne: { name: 'removePost' },
},
})

Relation mutations

Manage many-to-many relations with dedicated mutations. Configure in the relations option:

@GqlResolver(PostEntity, {
name: 'Post',
relations: {
tags: true,
postCategories: { include: ['isPrimary'] },
},
})

Two input types are used:

  • RelationIdInput (shared across all mutations) - only _id: ID!. Used by remove, since disconnect only needs to identify the link.
  • {Parent}{Relation}RelationInput (per-relation) - _id: ID! plus any extra pivot columns declared via include. Used by add and set.
# Shared, used by every remove mutation
input RelationIdInput {
_id: ID!
}
# Per-relation, used by add/set - includes extras declared via `include`
input PostPostCategoriesRelationInput {
_id: ID!
isPrimary: Boolean
}

Three operations are generated per relation:

Add

Connect items to the relation. Existing links are preserved.

mutation {
addTagsToPost(id: 1, items: [{ _id: 5 }, { _id: 6 }, { _id: 7 }]) {
success
}
}

Remove

Disconnect items from the relation. Uses the shared RelationIdInput - extras are not needed.

mutation {
removeTagsFromPost(id: 1, items: [{ _id: 5 }]) {
success
}
}

Set

Replace all links. Disconnects existing items and connects the new ones.

mutation {
setTagsOnPost(id: 1, items: [{ _id: 10 }, { _id: 20 }]) {
success
}
}

Extra pivot columns

When a join table has columns beyond the two foreign keys (e.g., isPrimary, order, role), expose them via include:

relations: {
postCategories: { include: ['isPrimary'] },
}

The generated input gains typed fields for each included column. NOT NULL columns without a default are required, everything else is optional.

mutation {
addPostCategoriesToPost(
id: 1
items: [{ _id: 5, isPrimary: true }, { _id: 6, isPrimary: false }]
) {
success
}
}

Naming pattern

OperationPatternExample
Addadd{Relation}To{Entity}addTagsToPost
Removeremove{Relation}From{Entity}removeTagsFromPost
Setset{Relation}On{Entity}setTagsOnPost

Selective enabling

Disable specific operations per relation:

relations: {
tags: { add: true, remove: true, set: false },
}

Combine include and selective ops:

relations: {
postCategories: { add: true, remove: true, set: false, include: ['isPrimary'] },
}

Set a relation to false to disable all its mutations:

relations: {
tags: true,
internalLinks: false,
}

Hooks

beforeRelation and afterRelation hooks fire for all relation operations and receive the full items array:

@Injectable()
export class PostHooks extends RelayerHooks<PostEntity, EM, AppContext> {
async beforeRelation(operation, relationName, items, ctx) {
this.logger.log(`${operation} ${relationName}: ${items.length} items`);
return items; // return modified items or void
}
async afterRelation(operation, relationName, items, ctx) {
await this.cacheService.invalidate(`post:${ctx.currentUser.id}`);
}
}

See Hooks & Context for the full hooks reference.