VibORM
Relations

One-to-Many

Define one-to-many relationships between models

One-to-Many Relation

A one-to-many relation connects a single record to multiple related records. This is the "one" side of the relationship.

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.relation.oneToMany(() => post),  // Array of posts
}).map("users");

// 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.relation
    .fields("authorId")
    .references("id")
    .manyToOne(() => user),
}).map("posts");

Characteristics

AspectValue
ReturnsArray (Post[])
FK locationOn the "many" side
Can be emptyYes (empty array)
Optional modifierNo (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
posts: s.relation.oneToMany(() => post)

// ❌ Wrong - oneToMany doesn't have FK
posts: s.relation.fields("???").oneToMany(() => post)

Minimal Configuration

One-to-many relations have minimal configuration since they don't own the FK:

s.relation.oneToMany(() => post)
// That's it! No .fields(), .references(), or .optional()

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.relation.oneToMany(() => post),
  // One user has many comments
  comments: s.relation.oneToMany(() => comment),
}).map("users");

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.relation
    .fields("authorId")
    .references("id")
    .manyToOne(() => user),
  // One post has many comments
  comments: s.relation.oneToMany(() => comment),
}).map("posts");

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.relation
    .fields("authorId")
    .references("id")
    .manyToOne(() => user),
  // Many comments belong to one post
  post: s.relation
    .fields("postId")
    .references("id")
    .manyToOne(() => post),
}).map("comments");

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: {}
    }
  }
});
// 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.relation.oneToMany(() => post),
}).map("categories");

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

Organization with Members

const organization = s.model({
  id: s.string().id().ulid(),
  name: s.string(),
  members: s.relation.oneToMany(() => membership),
}).map("organizations");

const membership = s.model({
  id: s.string().id().ulid(),
  role: s.enum(["ADMIN", "MEMBER"]),
  orgId: s.string(),
  userId: s.string(),
  organization: s.relation
    .fields("orgId")
    .references("id")
    .manyToOne(() => organization),
  user: s.relation
    .fields("userId")
    .references("id")
    .manyToOne(() => user),
}).map("memberships");

On this page