Skip to content
VibORM
Esc
navigateopen⌘Jpreview
On this page

One-to-Many

Define one-to-many relationships connecting a single record to multiple related records

A one-to-many relationship is what an s.toMany slot paired with an s.toOne slot spells, read from the collection side. The same pair read from the singular side is many-to-one.

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.toMany(() => post),  // Array of posts
});

// Each post belongs to one user (the singular side owns the FK)
const post = s.model({
  id: s.string().id().ulid(),
  title: s.string(),
  authorId: s.string(),
  author: s.toOne(() => 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 None exists — arrays are never null

No FK on This Side

This side does NOT own a foreign key, so it needs no configuration. All FK setup lives on the many-to-one side:

// ✅ Correct - no FK configuration needed
posts: s.toMany(() => post)

// ❌ Does not compile - a collection slot has no fields/references
posts: s.toMany(() => post).fields("???")

A collection slot paired with a singular one exposes .name(). (The junction methods — .through(), .source(), .target(), .onDelete(), .onUpdate() — belong to a toMany whose partner is also a toMany; see many-to-many.) See the relation method reference.

The singular partner is not optional scaffolding: a toMany whose target model declares nothing back is refused with R002, and if neither endpoint completes .fields(...).references(...) the edge is refused with FK004.

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.toMany(() => post),
});

const post = s.model({
  id: s.string().id().ulid(),
  title: s.string(),
  categoryId: s.string(),
  category: s.toOne(() => category)
    .fields("categoryId")
    .references("id"),
});

Was this page helpful?