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

      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.


If you have a relation that cannot change (for example, a comment can't change its author), 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

Relation API

In the example above, 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


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

withObservables(['comment'], ({ comment }) => ({
  author:, // shortcut syntax for `author:`

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).


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

const author = await

// Shortcut syntax:
const author = await

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


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 =


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

await database.get('comments').create(comment => {
  // ...

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(() => { = userId

Advanced relations

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' },

  authors = this.collections
    .query(Q.on('post_authors', 'post_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' },
  @immutableRelation('posts', 'post_id') post
  @immutableRelation('users', 'user_id') user

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

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

  posts = this.collections
    .query(Q.on('post_authors', 'user_id',;

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

Next steps

➡️ Now the last step of this guide: understand Writers (and Readers)