Relations
One-to-Many
Define one-to-many relationships connecting a single record to multiple related records
Basic Example
import { s } from "viborm";
// One user has many posts
const user = s.model({
id: s.string().id().ulid(),
email: s.string().unique(),
posts: s.oneToMany(() => post), // Array of posts
});
// Each post belongs to one user (many-to-one side)
const post = s.model({
id: s.string().id().ulid(),
title: s.string(),
authorId: s.string(),
author: s.manyToOne(() => user)
.fields("authorId")
.references("id"),
});Characteristics
| Aspect | Value |
|---|---|
| Returns | Array (Post[]) |
| FK location | On the "many" side |
| Can be empty | Yes (empty array) |
| Optional modifier | No (arrays are never null) |
No FK on This Side
The one-to-many side does NOT have a foreign key. The FK is on the many-to-one side:
// ✅ Correct - no FK configuration needed
posts: s.oneToMany(() => post)
// ❌ Wrong - oneToMany doesn't have fields/references
posts: s.oneToMany(() => post).fields("???")Minimal Configuration
One-to-many relations have minimal configuration since they don't own the FK:
s.oneToMany(() => post)
// That's it! No .fields(), .references(), or .optional()Available Methods
| Method | Description |
|---|---|
.name(relationName) | Set a custom relation name |
That's it! All FK configuration is done on the manyToOne side.
Complete Example
const user = s.model({
id: s.string().id().ulid(),
email: s.string().unique(),
name: s.string(),
// One user has many posts
posts: s.oneToMany(() => post),
// One user has many comments
comments: s.oneToMany(() => comment),
});
const post = s.model({
id: s.string().id().ulid(),
title: s.string(),
content: s.string(),
authorId: s.string(),
// Many posts belong to one user
author: s.manyToOne(() => user)
.fields("authorId")
.references("id"),
// One post has many comments
comments: s.oneToMany(() => comment),
});
const comment = s.model({
id: s.string().id().ulid(),
content: s.string(),
authorId: s.string(),
postId: s.string(),
// Many comments belong to one user
author: s.manyToOne(() => user)
.fields("authorId")
.references("id"),
// Many comments belong to one post
post: s.manyToOne(() => post)
.fields("postId")
.references("id"),
});Querying One-to-Many
// Include posts when fetching user
const user = await client.user.findUnique({
where: { id: "user_123" },
include: { posts: true },
});
// user.posts: Post[]
// Filter and paginate included posts
const user = await client.user.findUnique({
where: { id: "user_123" },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: "desc" },
take: 10,
}
}
});
// Filter users by their posts
const authors = await client.user.findMany({
where: {
posts: {
some: { published: true } // Has at least one published post
}
}
});
// Users with ALL published posts
const consistentAuthors = await client.user.findMany({
where: {
posts: {
every: { published: true }
}
}
});
// Users with NO posts
const newUsers = await client.user.findMany({
where: {
posts: {
none: {}
}
}
});Creating Related Records
// Create user with posts
const user = await client.user.create({
data: {
email: "alice@example.com",
name: "Alice",
posts: {
create: [
{ title: "First Post", content: "Hello!" },
{ title: "Second Post", content: "World!" },
]
}
},
include: { posts: true },
});
// Add posts to existing user
await client.user.update({
where: { id: "user_123" },
data: {
posts: {
create: { title: "New Post", content: "Content" },
}
}
});Common Patterns
Blog with Categories
const category = s.model({
id: s.string().id().ulid(),
name: s.string().unique(),
posts: s.oneToMany(() => post),
});
const post = s.model({
id: s.string().id().ulid(),
title: s.string(),
categoryId: s.string(),
category: s.manyToOne(() => category)
.fields("categoryId")
.references("id"),
});Organization with Members
const organization = s.model({
id: s.string().id().ulid(),
name: s.string(),
members: s.oneToMany(() => membership),
});
const membership = s.model({
id: s.string().id().ulid(),
role: s.enum(["ADMIN", "MEMBER"]),
orgId: s.string(),
userId: s.string(),
organization: s.manyToOne(() => organization)
.fields("orgId")
.references("id"),
user: s.manyToOne(() => user)
.fields("userId")
.references("id"),
});