Skip to content

HasManyThrough Relationship

The HasManyThrough relationship represents a multi-hop or chained relationship accessed through intermediate models. It allows you to chain multiple relationships (even of different types) to reach a related set of records through one or more intermediary models.

Tip

HasManyThrough is not the same as ManyToMany. HasManyThrough is for chaining multiple relationships (of any type), while ManyToMany is specifically for a symmetric many-to-many relationship using a join table.

Explanation

For example, a User has many Comments through Posts:

In this example, you can access all users who commented on a user's posts by chaining through posts and comments.

Defining the Relationship

The relationship is defined in your model configuration under the relationships property as a tuple:

typescript
[HasManyThrough, chain: Array<RelationshipTuple>]

Parameters

  • HasManyThrough: The relationship type.
  • chain: An array of relationship tuples defining the path through intermediate models.

Example

typescript
{
  models: {
    users: {
      schema: '++id, email, createdAt, updatedAt',
      properties: ['id', 'email', 'password'],
      primaryKey: 'id',
      relationships: {
        posts: [HasMany, 'posts', 'user_id'],
        comments: [HasMany, 'comments', 'user_id'],
        commenters: [HasManyThrough, [ 
          [HasMany, 'posts', 'user_id'],      // First hop: User → Posts
          [HasMany, 'comments', 'post_id'],   // Second hop: Posts → Comments
          [BelongsTo, 'users', 'user_id'],    // Final hop: Comments → User
        ]] 
      }
    },
    posts: {
      schema: '++id, user_id, title, body',
      properties: ['id', 'user_id', 'title', 'body'],
      relationships: {
        comments: [HasMany, 'comments', 'post_id']
      }
    },
    comments: {
      schema: '++id, post_id, user_id, body',
      properties: ['id', 'post_id', 'user_id', 'body'],
      relationships: {
        user: [BelongsTo, 'users', 'user_id']
      }
    }
  }
}

Accessing the Relationship

typescript
const user = await User.find(1)
const commenters = await user.commenters

user.commenters.forEach(commenter => {
  commenter.onChange((updatedCommenter) => {
    console.log('Commenter updated:', updatedCommenter)
  })
})

user.onPropertyChange('commenters', (newCommenters, oldCommenters) => {
  console.log('Commenters collection changed:', { new: newCommenters, old: oldCommenters })
})

Note

HasManyThrough is for chaining multiple relationships (even of different types) to reach a related set of records through one or more intermediary models. This is different from ManyToMany, which is specifically for a symmetric many-to-many relationship using a join table. If you want to relate two models directly through a join table, use ManyToMany. If you want to traverse a chain of relationships, use HasManyThrough.