@farming-labs/orm

Relations

Relations describe how models connect at the library level.

In the current repo:

Relation helpers

One-to-one

One-to-one usually means:

Example

user: model({
  table: "users",
  fields: {
    id: id(),
    email: string().unique(),
  },
  relations: {
    profile: hasOne("profile", { foreignKey: "userId" }),
  },
});

profile: model({
  table: "profiles",
  fields: {
    id: id(),
    userId: string().unique().references("user.id"),
    bio: string().nullable(),
  },
  relations: {
    user: belongsTo("user", { foreignKey: "userId" }),
  },
});

Plain English

One-to-many

One-to-many usually means:

Example

user: model({
  table: "users",
  fields: {
    id: id(),
  },
  relations: {
    sessions: hasMany("session", { foreignKey: "userId" }),
  },
});

session: model({
  table: "sessions",
  fields: {
    id: id(),
    userId: string().references("user.id"),
    token: string().unique(),
  },
  relations: {
    user: belongsTo("user", { foreignKey: "userId" }),
  },
});

Plain English

Many-to-many

Many-to-many uses a join model.

Example

user: model({
  table: "users",
  fields: {
    id: id(),
  },
  relations: {
    organizations: manyToMany("organization", {
      through: "membership",
      from: "userId",
      to: "organizationId",
    }),
  },
});

organization: model({
  table: "organizations",
  fields: {
    id: id(),
    name: string(),
  },
});

membership: model({
  table: "memberships",
  fields: {
    id: id(),
    userId: string().references("user.id"),
    organizationId: string().references("organization.id"),
    role: string(),
  },
});

What through, from, and to mean

For this relation:

organizations: manyToMany("organization", {
  through: "membership",
  from: "userId",
  to: "organizationId",
});

the relation is read from the perspective of the current model, user:

That is why the reverse side would swap them.

Runtime selection examples

One-to-one

const user = await orm.user.findFirst({
  where: { email: "ada@farminglabs.dev" },
  select: {
    id: true,
    profile: {
      select: {
        bio: true,
      },
    },
  },
});

One-to-many

const user = await orm.user.findFirst({
  where: { email: "ada@farminglabs.dev" },
  select: {
    id: true,
    sessions: {
      select: {
        token: true,
      },
      take: 5,
    },
  },
});

Current generator note

Relation helpers are still most powerful in the live runtime layer, but generation is no longer field-only:

That is why the docs still separate runtime relation behavior from generated ORM artifacts: direct relations are covered, but join-table convenience traversal is still a runtime concern.