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 distinctive rule of one-to-many: this side does NOT own a foreign key, so it needs almost no configuration. All FK setup lives 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("???")
The only available method is .name(relationName) for a custom relation name — see the relation method reference.
Querying One-to-Many
// Include posts, filtered and paginated
const user = await client.user.findUnique({
where: { id: "user_123" },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: "desc" },
take: 10,
}
}
});
// user.posts: Post[]
// Filter users by their posts
const authors = await client.user.findMany({
where: {
posts: {
some: { published: true } // Has at least one published post
}
}
});
See To-Many Relation Filters for some/every/none and Nested Writes for creating related records.
Common Pattern: 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"),
});