Nested Create
Creating related records inline within a single operation
Nested create lets you create related records inline — instead of creating records separately and then connecting them, you define the related data directly in the data object. All nested create operations are executed within a transaction to ensure atomicity: either all records are created, or none are.
Creating from the PK Side (Forward Relation)
When creating from the model that stores the FK, you can create the related record inline:
// Create a post and its author in one operation
const post = await client.db.Post.create({
data: {
title: 'My First Post',
author: {
create: {
name: 'John',
email: 'john@example.com',
isActive: true,
},
},
},
});Cerial creates the User first, then creates the Post with authorId automatically set to the new user's ID. You never provide the FK value manually — it's handled for you.
Creating from the Non-PK Side (Reverse Relation)
When creating from the model that is referenced by the FK, you can create child records that will automatically reference the new parent:
// Create a user and their posts in one operation
const user = await client.db.User.create({
data: {
name: 'John',
email: 'john@example.com',
isActive: true,
posts: {
create: [
{ title: 'First Post' },
{ title: 'Second Post' },
],
},
},
});The User is created first, then both Post records are created with authorId set to the new user's ID.
Single vs Array Relations
The shape of the create value depends on whether the relation is singular or an array:
| Relation Type | Create Syntax | Example |
|---|---|---|
Relation (singular) | { create: { ...fields } } | Forward 1 |
Relation[] (array) | { create: [{ ...fields }, ...] } | Reverse 1, N |
Singular Relation
const profile = await client.db.Profile.create({
data: {
bio: 'Hello world',
user: {
create: {
name: 'Alice',
email: 'alice@example.com',
isActive: true,
},
},
},
});Array Relation
const user = await client.db.User.create({
data: {
name: 'Alice',
posts: {
create: [
{ title: 'Post 1' },
{ title: 'Post 2' },
{ title: 'Post 3' },
],
},
},
});Mixing Create and Connect
For array relations, you can use create and connect together in the same operation — creating some records inline while linking to existing ones:
const user = await client.db.User.create({
data: {
name: 'Bob',
tags: {
create: [{ name: 'new-tag' }],
connect: [existingTagId],
},
},
});
// Creates a new Tag and connects to an existing Tag, all in one transactionMixing create and connect is only available for array relations. Singular relations accept either create or connect — not both — since the FK can only hold a single value.
Nested Create in Updates
Nested create is also available in update operations, from both the PK side and the reverse side:
// Add new posts to an existing user
await client.db.User.updateUnique({
where: { id: userId },
data: {
posts: {
create: [{ title: 'Another Post' }],
},
},
});
// Creates a new Post with authorId set to userId// Create a new author for a post that doesn't have one
await client.db.Post.updateUnique({
where: { id: postId },
data: {
author: {
create: {
name: 'New Author',
email: 'new@example.com',
isActive: true,
},
},
},
});
// Creates a new User, then sets Post.authorId to the new user's IDYou can also combine create with connect and disconnect in array relation updates:
await client.db.User.updateUnique({
where: { id: userId },
data: {
tags: {
create: [{ name: 'brand-new-tag' }],
connect: [existingTagId],
disconnect: [oldTagId],
},
},
});N with Nested Create
For many-to-many relations, nested create handles bidirectional sync automatically:
const user = await client.db.User.create({
data: {
name: 'Charlie',
tags: {
create: [
{ name: 'javascript' },
{ name: 'typescript' },
],
},
},
});This single operation:
- Creates the
User - Creates both
Tagrecords - Sets
User.tagIdsto the new tag IDs - Adds the new user's ID to each
Tag.userIds
All within a single transaction — both sides of the N
relationship are in sync.Type Safety
The create object is fully typed based on the target model's NestedCreate type. TypeScript enforces that all required fields are present and all field types match:
// ✅ Type error: 'email' is missing in type
const post = await client.db.Post.create({
data: {
title: 'My Post',
author: {
create: { name: 'John' }, // Error: Property 'email' is missing
},
},
});FK fields are auto-omitted
Record fields that are managed by a relation (like authorId on Post) are automatically excluded from the NestedCreate type. You never need to provide the FK value manually — Cerial handles it. This means the nested create object only asks for the fields that you control.
Required array fields on the target model that you don't provide in the nested create will automatically default to empty arrays — the same defaults that apply to regular create operations.
Transaction Guarantees
Nested create operations are wrapped in a transaction:
- If any part of the nested create fails (e.g., a unique constraint violation on a nested record), the entire operation is rolled back.
- The parent record is not created if a child record fails to create.
- For N relations, bidirectional sync is included in the same transaction.
Nested creates also work inside $transaction — all operations are covered by a single atomic transaction:
await client.$transaction(async (tx) => {
const user = await tx.User.create({
data: {
name: 'Alice',
posts: {
create: [{ title: 'First Post' }],
},
},
});
const anotherUser = await tx.User.create({
data: {
name: 'Bob',
posts: {
create: [{ title: 'Bob Post' }],
},
},
});
});
// Both users and all posts created atomically