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 viainclude. Used by add and set.
# Shared, used by every remove mutationinput 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
| Operation | Pattern | Example |
|---|---|---|
| Add | add{Relation}To{Entity} | addTagsToPost |
| Remove | remove{Relation}From{Entity} | removeTagsFromPost |
| Set | set{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.