Joins
SlintORM supports INNER, LEFT, RIGHT, and FULL OUTER joins. RIGHT and FULL OUTER are available via model.advanced().
INNER join
// .join(table, left, op, right) — INNER JOIN
const posts = await Post.query()
.join('users', 'posts.userId', '=', 'users.id')
.select('posts.id', 'posts.title', 'users.name', 'users.email')
.get();
// SELECT posts.id, posts.title, users.name, users.email
// FROM posts
// INNER JOIN users ON posts.userId = users.idLEFT join
// .leftJoin(table, left, op, right) — LEFT JOIN
// Returns all posts, even those without a matching user
const posts = await Post.query()
.leftJoin('users', 'posts.userId', '=', 'users.id')
.select('posts.*', 'users.name')
.get();RIGHT join
// .rightJoin(table, left, op, right) — RIGHT JOIN
// Available via advanced() (AdvancedQueryBuilder)
const data = await Post.advanced()
.rightJoin('users', 'posts.userId', '=', 'users.id')
.select('users.name', 'posts.title')
.get();FULL OUTER join
// .fullOuterJoin(table, left, op, right) — FULL OUTER JOIN
// Available via advanced() (AdvancedQueryBuilder)
// Note: SQLite does not support FULL OUTER JOIN natively
const data = await Post.advanced()
.fullOuterJoin('users', 'posts.userId', '=', 'users.id')
.get();Multiple joins
// Multiple joins
const results = await Post.query()
.join('users', 'posts.userId', '=', 'users.id')
.join('categories', 'posts.categoryId', '=', 'categories.id')
.select(
'posts.id',
'posts.title',
'users.name',
'categories.name'
)
.where('users.role', '=', 'author')
.orderBy('posts.createdAt', 'DESC')
.get();Complex conditions with whereRaw
// Complex join conditions with whereRaw
const results = await Post.query()
.join('post_tags', 'posts.id', '=', 'post_tags.postId')
.join('tags', 'post_tags.tagId', '=', 'tags.id')
.whereRaw('tags.name IN (?, ?)', ['typescript', 'orm'])
.select('posts.id', 'posts.title')
.get();Auto-qualification
When joins are active and no explicit select() is provided, SlintORM automatically qualifies the root table to table.*to avoid ambiguous column errors on shared names like id.
// When joins are present and no .select() is called,
// SlintORM auto-qualifies root table columns to "table.*"
// to prevent "ambiguous column" errors from the database
const posts = await Post.query()
.join('users', 'posts.userId', '=', 'users.id')
// No .select() — SlintORM emits: SELECT posts.* FROM posts ...
.get();Prefer preloads over joins for relations
Manual joins are best for arbitrary SQL relationships. For loading related records declared in model annotations (@relation), use .preload("relation") instead. Preloads batch-fetch related records without N+1 queries.