Relation Traversal
SlintORM provides three methods for filtering across relations without writing manual JOIN SQL. All three read from your @relation annotations to generate queries automatically.
throughRelation(path)
Takes a dot-separated path of relation names and auto-generates INNER JOINs from schema metadata. You still apply the final .where() on the joined table column.
// .throughRelation(path)
// Dot-separated path of relation names
// Auto-generates JOINs from schema metadata — no manual ON clauses
// Get assessments taken by a specific student
// Path: enrollments -> assessments (via enrollment relation)
const assessments = await Assessment.query()
.throughRelation('enrollment.student')
.where('students.id', '=', 42)
.get();
// SlintORM reads your @relation annotations and builds:
// INNER JOIN enrollments ON enrollments.assessmentId = assessments.id
// INNER JOIN students ON students.id = enrollments.studentIdwhereRelated(path, column, value)
Combines throughRelation with an IN-subquery resolution. Cleaner than building joins manually for filter-by-relation patterns.
// .whereRelated(path, column, value)
// Combines throughRelation with an IN-subquery resolution
// Cleaner than building joins manually for filter-by-relation patterns
const assessments = await Assessment.query()
.whereRelated('enrollment.student', 'id', 42)
.get();
// WHERE assessments.id IN (
// SELECT assessmentId FROM enrollments
// WHERE studentId IN (SELECT id FROM students WHERE id = 42)
// )relatedTo(targetModelName, column, value)
The most declarative option. Uses BFS to automatically discover the shortest relation path between the current model and the named target model. You don't need to know the path.
// .relatedTo(targetModelName, column, value)
// BFS — automatically discovers the shortest path between
// the current model and the target model through the relation graph
// No need to know the path — SlintORM finds it
const assessments = await Assessment.query()
.relatedTo('Student', 'id', 42)
.get();
// SlintORM discovers: Assessment -> Enrollment -> Student
// and builds the appropriate subquery chainSide-by-side comparison
// All three methods solving the same problem:
// "Get assessments for student with id = 42"
// 1. throughRelation — explicit path, generates JOINs
const a = await Assessment.query()
.throughRelation('enrollment.student')
.where('students.id', '=', 42)
.get();
// 2. whereRelated — explicit path, generates IN subquery
const b = await Assessment.query()
.whereRelated('enrollment.student', 'id', 42)
.get();
// 3. relatedTo — BFS path discovery, most declarative
const c = await Assessment.query()
.relatedTo('Student', 'id', 42)
.get();Real-world examples
// relatedTo in a real-world scenario:
// Find all posts by users who belong to a given team
const teamPosts = await Post.query()
.relatedTo('Team', 'id', teamId)
.where('published', '=', true)
.orderBy('createdAt', 'DESC')
.get();
// SlintORM BFS finds: Post -> User -> team_members -> Team
// and builds the appropriate JOIN or subquery chain
// whereRelated for the same query with explicit path:
const teamPosts2 = await Post.query()
.whereRelated('user.teams', 'id', teamId)
.where('published', '=', true)
.get();Use relatedTo when you want maximum simplicity and the relation graph is well-defined. Use whereRelated when you need an explicit path (e.g. two possible paths exist). Use throughRelation when you need the joined table columns accessible in where() or select().