Skip to main content
Version: 0.27.1

Model

A Model class represents a type of thing in your app. For example, Post, Comment, User.

Before defining a Model, make sure you defined its schema.

Create a Model

Let's define the Post model:

// model/Post.js
import { Model } from '@nozbe/watermelondb'

export default class Post extends Model {
static table = 'posts'
}

Specify the table name for this Model — the same you defined in the schema.

Now add the new Model to Database:

// index.js
import Post from 'model/Post'

const database = new Database({
// ...
modelClasses: [Post],
})

Associations

Many models relate to one another. A Post has many Comments. And every Comment belongs to a Post. (Every relation is double-sided). Define those associations like so:

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

class Comment extends Model {
static table = 'comments'
static associations = {
posts: { type: 'belongs_to', key: 'post_id' },
}
}

On the "child" side (comments) you define a belongs_to association, and pass a column name (key) that points to the parent (post_id is the ID of the post the comment belongs to).

On the "parent" side (posts) you define an equivalent has_many association and pass the same column name (⚠️ note that the name here is foreignKey).

Add fields

Next, define the Model's fields (properties). Those correspond to table columns defined earlier in the schema.

import { field, text } from '@nozbe/watermelondb/decorators'

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

@text('title') title
@text('body') body
@field('is_pinned') isPinned
}

Fields are defined using ES6 decorators. Pass column name you defined in Schema as the argument to @field.

Field types. Fields are guaranteed to be the same type (string/number/boolean) as the column type defined in Schema. If column is marked isOptional: true, fields may also be null.

User text fields. For fields that contain arbitrary text specified by the user (e.g. names, titles, comment bodies), use @text - a simple extension of @field that also trims whitespace.

Note: Why do I have to type the field/column name twice? The database convention is to use snake_case for names, and the JavaScript convention is to use camelCase. So for any multi-word name, the two differ. Also, for resiliency, we believe it's better to be explicit, because over time, you might want to refactor how you name your JavaScript field names, but column names must stay the same for backward compatibility.

Date fields

For date fields, use @date instead of @field. This will return a JavaScript Date object (instead of Unix timestamp integer).

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

class Post extends Model {
// ...
@date('last_event_at') lastEventAt
}

Derived fields

Use ES6 getters to define model properties that can be calculated based on database fields:

import { field, text } from '@nozbe/watermelondb/decorators'

class Post extends Model {
static table = 'posts'

@date('archived_at') archivedAt

get isRecentlyArchived() {
// in the last 7 days
return this.archivedAt &&
this.archivedAt.getTime() > Date.now() - 7 * 24 * 3600 * 1000
}
}

To-one relation fields

To point to a related record, e.g. Post a Comment belongs to, or author (User) of a Comment, use @relation or @immutableRelation:

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

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

➡️ Learn more: Relation API

Children (to-many relation fields)

To point to a list of records that belong to this Model, e.g. all Comments that belong to a Post, you can define a simple Query using @children:

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

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

@children('comments') comments
}

Pass the table name of the related records as an argument to @children. The resulting property will be a Query you can fetch, observe, or count.

Note: You must define a has_many association in static associations for this to work

➡️ Learn more: Queries

Custom Queries

In addition to @children, you can define custom Queries or extend existing ones, for example:

import { children } from '@nozbe/watermelondb/decorators'
import { Q } from '@nozbe/watermelondb'

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

@children('comments') comments
@lazy verifiedComments = this.comments.extend(
Q.where('is_verified', true)
)
}

➡️ Learn more: Queries

Writer methods

Define writers to simplify creating and updating records, for example:

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

class Comment extends Model {
static table = 'comments'

@field('is_spam') isSpam

@writer async markAsSpam() {
await this.update(comment => {
comment.isSpam = true
})
}
}

Methods must be marked as @writer to be able to modify the database.

➡️ Learn more: Writers

Advanced fields

You can also use these decorators:

  • @json for complex serialized data
  • @readonly to make the field read-only
  • @nochange to disallow changes to the field after the first creation

And you can make observable compound properties using RxJS...

➡️ Learn more: Advanced fields


Next steps

➡️ After you define some Models, learn the Create / Read / Update / Delete API