SlintORM
Docsnpm

Preloads

.preload("relation") eager-loads related records declared in your model annotations. All four relation kinds are supported. Records are batch-fetched with a single IN query per relation — no N+1.

Basic preload

// .preload("relation") — eager-load a declared relation
// Batch-fetched in a single IN query — no N+1

const posts = await Post.query()
  .preload('user')   // loads each post's user
  .get();

// posts[0].user is the full User object
console.log(posts[0].user?.name);

One-to-many

// onetomany — loads child records per parent
// @relation onetomany:Comment;foreignKey:postId

const posts = await Post.query()
  .preload('comments')
  .get();

// posts[0].comments is Comment[]

Many-to-one

// manytoone — loads the parent record
// @relation manytoone:User;foreignKey:userId

const comments = await Comment.query()
  .preload('user')
  .get();

// comments[0].user is the User object

One-to-one

// onetoone — loads the single related record
// @relationship onetoone:Profile;foreignKey:userId

const users = await User.query()
  .preload('profile')
  .get();

// users[0].profile is Profile | null

Many-to-many

Many-to-many relations fetch pivot data in a single INNER JOIN query against the through table.

// manytomany — uses a single INNER JOIN on the pivot table
// @relation manytomany:Team;through:team_members;foreignKey:userId;relatedKey:teamId

const users = await User.query()
  .preload('teams')
  .get();

// users[0].teams is Team[]

Nested preloads

Use dot-notation to preload at any depth. Cycle detection prevents infinite loops.

// Nested preloads — dot-notation, any depth
const posts = await Post.query()
  .preload('user')           // loads post.user
  .preload('user.profile')   // loads post.user.profile
  .get();

// Cycle detection is built-in — circular chains stop automatically

// Another example: posts with comments and each comment's author
const detailed = await Post.query()
  .preload('comments')
  .preload('comments.user')
  .get();

exclude() on preloads

Strip sensitive columns from root or any nested preloaded relation using dot-notation in exclude().

// .exclude() strips columns from preloaded relations
const posts = await Post.query()
  .preload('user')
  .exclude('user.password')
  .exclude('user.secretToken')
  .get();

// posts[0].user exists but has no .password or .secretToken field

// exclude() on root and nested in one chain
const users = await User.query()
  .preload('posts')
  .preload('posts.comments')
  .exclude('password')          // root User
  .exclude('posts.body')        // strip from Post
  .exclude('posts.comments.ip') // strip from nested Comment
  .get();
How batching works

For a one-to-many preload, SlintORM collects all parent IDs from the root query result, then fires one WHERE id IN (...) query and distributes records back to their parents. This means N posts with comments = 2 queries total, not N+1.

PreviousOrdering & PaginationNextAggregates