Skip to main content

Relations Overview

Hysteria ORM supports rich relation types between models:

RelationDescription
belongsToInverse of hasOne/hasMany
hasOneOne-to-one relationship
hasManyOne-to-many relationship
manyToManyMany-to-many via join table

Relations use batch loading—one query per relation, only when load is called.

caution

Loading too many relations can slow down your query. Be selective about what you load.

Example Models

class User extends Model {
@hasMany(() => Post, 'userId')
declare posts: Post[];

@manyToMany(() => Address, () => UserAddress, {
leftForeignKey: 'userId',
rightForeignKey: 'addressId',
})
declare addresses: Address[];
}

class Post extends Model {
@column({ type: 'integer', primaryKey: true })
declare id: number;

@column({ type: 'integer' })
declare userId: number;

// Belongs to does not benefit from type inference by default for typescript limitations, it's adviced to use the generic type to avoid type errors
@belongsTo<typeof Post>(() => User, 'userId') // With the generic we are obligated to specify a model field
declare user: User;
}

class Address extends Model {
@manyToMany(() => User, () => UserAddress, {
leftForeignKey: 'addressId',
rightForeignKey: 'userId',
})
declare users: User[];
}

Querying Relations

important

Always select the foreign key in the relation query builder, otherwise the relation will not be filled.

Eager Loading

const users = await User.query().load('posts').many();

Selecting Columns

The foreign key (userId) must be selected for relations to work:

const users = await User.query().load('posts', (qb) =>
qb.select('id', 'title', 'userId')
).many();

Nested Relations

const users = await User.query().load('posts', (qb) =>
qb.load('user')
).many();

Filtering on Relations

const users = await User.query().load('posts', (qb) =>
qb.where('title', 'Hello World')
).many();

Limit and Offset

Limit and offset apply to the related models. Adding limit or offset creates a CTE internally.

const users = await User.query().load('posts', (qb) =>
qb.where('title', 'Hello World').limit(10).offset(10)
).many();

Advanced relation queries

const users = await User.query().load('posts', (qb) =>
qb.annotate("max", "id", "maxId").load("user")
).many();

Best Practices

  • Use load for batch relation loading.
  • Use callbacks for nested and filtered relations.
  • Always define foreign keys explicitly for clarity.

Next: Advanced SQL Features