Relations

A Relation object represents one record pointing to another — such as the author (User) of a Comment, or the Post the comment belongs to.

Defining Relations

There's two steps to defining a relation:

  1. A table column for the related record's ID

    tableSchema({
      name: 'comments',
      columns: [
        // ...
        { name: 'author_id', type: 'string' },
      ]
    }),
    
  2. A @relation field defined on a Model class:

    import { relation } from '@nozbe/watermelondb/decorators'
    
    class Comment extends Model {
      // ...
      @relation('users', 'author_id') author
    }
    

    The first argument is the table name of the related record, and the second is the column name with an ID for the related record.

Relation API

In the example above, comment.author returns a Relation object.

Remember, WatermelonDB is a lazily-loaded database, so you don't get the related User record immediately, only when you explicitly fetch it

Observing

Most of the time, you connect Relations to Components by using observe() (the same as with Queries):

withObservables(['comment'], ({ comment }) => ({
  comment: comment.observe(),
  author: comment.author.observe(),
}))

The component will now have an author prop containing a User, and will re-render both when the user changes (e.g. comment's author changes its name), but also when a new author is assigned to the comment (if that was possible).

Fetching

To simply get the related record, use fetch. You might need it in Actions

const author = await comment.author.fetch()

// Shortcut syntax:
const author = await comment.author

Note: If the relation column (in this example, author_id) is marked as isOptional: true, fetch() might return null.

ID

If you only need the ID of a related record (e.g. to use in an URL or for the key= React prop), use id.

const authorId = comment.author.id

Assigning

Use set() to assign a new record to the relation

await commentsCollection.create(comment => {
  comment.author.set(someUser)
  // ...
})

Note: you can only do this in the .create() or .update() block.

You can also use set id if you only have the ID for the record to assign

await comment.update(() => {
  comment.author.id = userId
})

Advanced relations

immutableRelation

If you have a relation that cannot change (for example, a comment can't change its author), you can use @immutableRelation for extra protection and performance:

import { immutableRelation } from '@nozbe/watermelondb/decorators'

class Comment extends Model {
  // ...
  @immutableRelation('posts', 'post_id') post
  @immutableRelation('users', 'author_id') author
}

Many-To-Many Relation

If for instance, our app Posts can be authored by many Users and a user can author many Posts. We would create such a relation following these steps:-

  1. Create a pivot schema and model that both the User model and Post model has association to; say PostAuthor
  2. Create has_many association on both User and Post pointing to PostAuthor Model
  3. Create belongs_to association on PostAuthor pointing to both User and Post
  4. Retrieve all Posts for a user by defining a query that uses the pivot PostAuthor to infer the Posts that were authored by the User.
import {lazy } from '@nozbe/watermelondb/decorators'

class Post extends Model {
  static table = 'posts'
  static associations = {
    post_authors: { type: 'has_many', foreignKey: 'post_id' },
  }

  @lazy
  authors = this.collections
    .get('users')
    .query(Q.on('post_authors', 'post_id', this.id));
}
import { field } from '@nozbe/watermelondb/decorators'

class PostAuthor extends Model {
  static table = 'post_authors'
  static associations = {
    posts: { type: 'belongs_to', key: 'post_id' },
    users: { type: 'belongs_to', key: 'user_id' },
  }
  @field('post_id') postId
  @field('user_id') userId
}

import {lazy } from '@nozbe/watermelondb/decorators'

class User extends Model {
  static table = 'users'
  static associations = {
    post_authors: { type: 'has_many', foreignKey: 'user_id' },
  }

  @lazy
  posts = this.collections
    .get('posts')
    .query(Q.on('post_authors', 'user_id', this.id));

}
withObservables(['post'], ({ post }) => ({
  authors: post.authors.observe(),
}))

Next steps

➡️ Now the last step of this guide: define custom Actions