Search code examples
react-nativeexpowatermelondb

Using @writer or @reader methods in WatermelonDB outside of the model class


I'm trying to use the concept of @writer methods and I just can't get it to work. The @writer methods have been defined in my model class, and I now expect to be able to use them in other components.

I've created these writer methods like so:

import { Collection, Model } from '@nozbe/watermelondb';
import { field, text, writer } from '@nozbe/watermelondb/decorators';
import PostType from '@/data/types/post';

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

  @field('post_id') postId!: number;
  @field('row_modified_at') rowModifiedAt!: string;
  @field('title') title!: string;
  @text('body') body!: string;

  @writer async createPost(postData: PostType) {
    const postCollection: Collection<Post> = this.collections.get('posts');
    const newPost = await postCollection.create((post) => {
      post.postId = postData.postId;
      post.rowModifiedAt = postData.rowModifiedAt;
      post.title = postData.title;
      post.body = postData.body;
    });
    return newPost;
  }

  @writer async updatePost(postData: PostType) {
    const postCollection: Collection<Post> = this.collections.get('posts');
    const post = await postCollection.find(String(postData.postId));
    await post.update((post) => {
      post.postId = postData.postId;
      post.rowModifiedAt = postData.rowModifiedAt;
      post.title = postData.title;
      post.body = postData.body;
    });
  }

I'm then trying to use them in a screen component

export const Style = () => {
const database = useDatabase();
const postCollection = database.collections.get<Post>('posts');

const createPost = async () => {

try {
  await postCollection ({  <--- I can't find the @writer method to utilise it
    postId: newPost.postId,
    rowModifiedAt: newPost.rowModifiedAt,
    title: newPost.title,
    body: newPost.body,
  });
  console.log('Post created successfully');
} catch (error) {
  console.error('Error creating post:', error);
}

I'm using TypeScript and Expo


Solution

  • Just because this works, that does not mean you should do this. Looking at the model tests, you can do something like the following

    let newPost = new postCollection(this.props.database.get('posts'), {});
    await newPost.createPost({ /* Data here */ });
    

    HOWEVER, the Model class's constructor (which is the class that is extended in each table's class) has a comment that reads: (github link)

    Don't use this directly! Use collection.create()

    The documentation for writers has a section for inline writers. It currently has the following snippet. Notice that the .create() call is in an async function passed to the database.write method. This can be used outside of models to write data.

    const newPost = await database.write(async => {
      const post = await database.get('posts').create(post => {
        post.title = 'New post'
        post.body = 'Lorem ipsum...'
      })
      const comment = await database.get('comments').create(comment => {
        comment.post.set(post)
        comment.author.id = someUserId
        comment.body = 'Great post!'
      })
    
      // Note: Value returned from the wrapped function will be returned to `database.write` caller
      return post
    })
    

    The documentation points out that by putting most of your database writing function inside of the model, it centralizes your database code, but it does not point out any ideal way to ONLY write through writer decorated methods. From what I have seen, you have to get some data into the database before you can utilize writer decorated methods.