Mutations
The CRUD resolver automatically exposes mutation endpoints for create, update, delete, and upsert operations. All mutations include built-in authorization, validation, and context filtering.
What You’ll Learn
Section intitulée « What You’ll Learn »This guide will help you understand:
- ✅ Available mutation operations
- ✅ How to create, update, and delete records
- ✅ How to use upsert operations
- ✅ How to handle translations in mutations
- ✅ How to use custom input DTOs
Overview
Section intitulée « Overview »The CRUD resolver automatically exposes mutation endpoints. The endpoint names are derived from the name provided to @ObjectType or the class name.
Example DTO
Section intitulée « Example DTO »The following examples are based on this ProductDTO:
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('Product')export class ProductDTO { @IDField(() => ID) id!: string;
@EnhancedField() name!: string;
@EnhancedField() price!: number;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}Available Mutations
Section intitulée « Available Mutations »For the Product DTO, the following mutations are automatically generated:
| Mutation | Description | Use Case |
|---|---|---|
createOneProduct |
Create a single record | Single record creation |
createManyProducts |
Create multiple records | Bulk creation |
updateOneProduct |
Update a single record by ID | Single record update |
updateManyProducts |
Update multiple records with filter | Bulk update |
upsertOneProduct |
Create or update a single record | Idempotent operations |
upsertManyProducts |
Create or update multiple records | Bulk upsert |
deleteOneProduct |
Delete one record by ID | Single record deletion |
deleteManyProducts |
Delete multiple records with filter | Bulk deletion |
Create Operations
Section intitulée « Create Operations »Create One
Section intitulée « Create One »Create a single product:
GraphQL:
mutation { createOneProduct(input: { product: { name: "Premium Eggs", price: 15.99 } }) { id name price created updated }}Response:
{ "data": { "createOneProduct": { "id": "1", "name": "Premium Eggs", "price": 15.99, "created": "2020-01-14T09:01:35.834Z", "updated": "2020-01-14T09:01:35.834Z" } }}Create Many
Section intitulée « Create Many »Create multiple products at once:
GraphQL:
mutation { createManyProducts( input: { products: [{ name: "Organic Eggs", price: 18.99 }, { name: "Free Range Eggs", price: 20.99 }] } ) { id name price created updated }}Response:
{ "data": { "createManyProducts": [ { "id": "2", "name": "Organic Eggs", "price": 18.99, "created": "2020-01-14T09:01:55.110Z", "updated": "2020-01-14T09:01:55.110Z" }, { "id": "3", "name": "Free Range Eggs", "price": 20.99, "created": "2020-01-14T09:01:55.110Z", "updated": "2020-01-14T09:01:55.110Z" } ] }}Update Operations
Section intitulée « Update Operations »Update One
Section intitulée « Update One »Update a single product by ID:
GraphQL:
mutation { updateOneProduct(input: { id: "1", update: { price: 16.99 } }) { id name price created updated }}Response:
{ "data": { "updateOneProduct": { "id": "1", "name": "Premium Eggs", "price": 16.99, "created": "2020-01-14T07:00:31.763Z", "updated": "2020-01-14T09:02:28.167Z" } }}Update Many
Section intitulée « Update Many »Update multiple products using a filter:
Important
The filter CANNOT be an empty object. This prevents accidental updating of all records.
GraphQL:
mutation { updateManyProducts(input: { filter: { id: { in: ["1", "2"] } }, update: { price: 17.99 } }) { updatedCount }}Response:
{ "data": { "updateManyProducts": { "updatedCount": 2 } }}Upsert Operations
Section intitulée « Upsert Operations »Upsert One
Section intitulée « Upsert One »Create or update a single product (useful for idempotent operations):
GraphQL:
mutation { upsertOneProduct(input: { product: { id: "1", name: "Premium Eggs", price: 15.99 } }) { id name price created updated }}Response:
{ "data": { "upsertOneProduct": { "id": "1", "name": "Premium Eggs", "price": 15.99, "created": "2020-01-14T07:00:31.763Z", "updated": "2020-01-14T09:03:15.234Z" } }}Upsert Many
Section intitulée « Upsert Many »Create or update multiple products:
GraphQL:
mutation { upsertManyProducts( input: { products: [ { id: "1", name: "Premium Eggs", price: 15.99 } { id: "2", name: "Organic Eggs", price: 18.99 } ] } ) { id name price created updated }}Managing Relations in Mutations
Section intitulée « Managing Relations in Mutations »When using upsertOne mutations, you can automatically manage related records. The system supports two types of relations:
- “One” relations (many-to-one, one-to-one): Stored via foreign key in main table
- “Many” relations (one-to-many, many-to-many): Stored in separate relation table
Example: Upsert with Relations
Section intitulée « Example: Upsert with Relations »GraphQL:
mutation { upsertOneOrganization( input: { organization: { id: "org-123" name: "My Organization" logo: { id: "file-456" } memberships: [{ userId: "user-1", type: OWNER }, { userId: "user-2", type: MEMBER }] } } ) { id name logo { id name } memberships { userId type } }}Behavior:
- Creates or updates the organization
- Sets the logo (one relation) by updating
logoFileId - Creates, updates, or deletes memberships (many relation) based on the input
Configuration
Section intitulée « Configuration »Relation management is configured using decorator options on relation fields:
@ObjectType()@Relation('logo', () => File, { nullable: true, foreignKeyFieldName: 'logoFileId', // Optional: auto-detected})@UnPagedRelation('memberships', () => OrganizationMembership, { nullable: true, preserveExisting: false, // If true, preserves existing records not in input conflictColumns: ['userId', 'organizationId'], // Optional: auto-detected})export class Organization { // ...}Learn more: Managing Relations in Mutations
Delete Operations
Section intitulée « Delete Operations »Delete One
Section intitulée « Delete One »Delete a single product by ID:
GraphQL:
mutation { deleteOneProduct(input: { id: "1" }) { id name price created updated }}Delete Many
Section intitulée « Delete Many »Delete multiple products using a filter:
GraphQL:
mutation { deleteManyProducts(input: { filter: { name: { like: "Premium%" } } }) { deletedCount }}Response:
{ "data": { "deleteManyProducts": { "deletedCount": 2 } }}Advanced Features
Section intitulée « Advanced Features »Automatic Filtering
Section intitulée « Automatic Filtering »The @ContextFilter() decorator automatically applies filters to mutations, ensuring users can only create, update, or delete records they have access to.
Learn more: ContextFilter Documentation
Translations in Mutations
Section intitulée « Translations in Mutations »When creating or updating records with localizable fields, you can provide translations using the translations field.
Example: Product Input with Translations
Section intitulée « Example: Product Input with Translations »import { IDField, TranslationInputType } from '@lib/type-graphql-query';import { ObjectType, ID, InputType, Field } from '@lib/type-graphql';import { EnhancedField } from '@lib/type-graphql-query';import { Translation } from '@lib/type-graphql-query';
@ObjectType('Product')export class Product { @IDField(() => ID) id!: string;
@EnhancedField({ localizable: true }) name!: string;
@EnhancedField({ localizable: true }) description!: string;
@EnhancedField() price!: number;}
// Get the translation input type automatically generated from Productconst ProductTranslationInput = TranslationInputType(Product);
@InputType()export class ProductInput { @Field({ nullable: true }) name?: string;
@Field({ nullable: true }) description?: string;
@Field({ nullable: true }) price?: number;
@Field(() => [ProductTranslationInput], { nullable: true, defaultValue: [] }) translations?: Translation<Product, 'name' | 'description'>[];}Create One with Translations
Section intitulée « Create One with Translations »GraphQL:
mutation { createOneProduct( input: { product: { price: 15.99 translations: [ { locale: "fr", name: "Œufs Premium", description: "Œufs de haute qualité" } { locale: "en", name: "Premium Eggs", description: "High quality eggs" } { locale: "mg", name: "Atody Premium", description: "Atody kalitao avo lenta" } ] } } ) { id name description price }}Locale Parameter
Section intitulée « Locale Parameter »All mutations accept an optional locale parameter that specifies the default locale:
GraphQL:
mutation { createOneProduct(input: { product: { name: "Premium Eggs", price: 15.99 } }, locale: "fr") { id name price }}When locale is provided, it will be used as the default locale for any localizable fields that don’t have explicit translations.
Custom Input DTOs
Section intitulée « Custom Input DTOs »You can specify custom input DTOs for create and update operations:
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, InputType, Field } from '@lib/type-graphql';import { Translation } from '@lib/type-graphql-query';
@ObjectType('Product')export class ProductDTO { @IDField(() => ID) id!: string;
@EnhancedField() name!: string;
@EnhancedField() price!: number;}
// Get the translation input typeconst ProductTranslationInput = TranslationInputType(Product);
@InputType()export class CreateProductInput { @Field() name!: string;
@Field() price!: number;
@Field(() => [ProductTranslationInput], { nullable: true, defaultValue: [] }) translations?: Translation<Product, 'name' | 'description'>[];}
@InputType()export class UpdateProductInput { @Field({ nullable: true }) name?: string;
@Field({ nullable: true }) price?: number;
@Field(() => [ProductTranslationInput], { nullable: true, defaultValue: [] }) translations?: Translation<Product, 'name' | 'description'>[];}Then specify these in your resolver options:
import { buildTypeDefsAndResolvers } from '@lib/type-graphql-query';import { Product } from './dto/product.type';import { CreateProductInput } from './dto/create-product.input';import { UpdateProductInput } from './dto/update-product.input';import { product as productTable } from '@lib/db';
const { resolvers, typeDefs } = await buildTypeDefsAndResolvers({ resolvers: [ { DTOClass: Product, EntityClass: productTable, CreateDTOClass: CreateProductInput, UpdateDTOClass: UpdateProductInput, }, ],});Quick Reference
Section intitulée « Quick Reference »| Operation | Use Case | Documentation |
|---|---|---|
createOne |
Create a single record | This guide |
createMany |
Create multiple records | This guide |
updateOne |
Update a single record | This guide |
updateMany |
Update multiple records | This guide |
upsertOne |
Create or update | This guide |
upsertMany |
Bulk upsert | This guide |
deleteOne |
Delete a single record | This guide |
deleteMany |
Delete multiple records | This guide |
Next Steps
Section intitulée « Next Steps »Continue learning:
- 🎯 ContextFilter - Automatic filtering for queries and mutations
- 🔍 Queries - Query operations
- 🔐 Authorization - Authorization and permissions
- 🌍 Translations - Multi-language support
Related Topics
Section intitulée « Related Topics »- Type GraphQL Query Overview - Complete overview
- DTOs - Learn about DTO definitions
- Queries - Query operations
- Authorization - Access control