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

316 Views Asked by At

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

2

There are 2 best solutions below

0
Alex O'Connor On BEST ANSWER

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.

0
Iam Damba On

I think i find a solution to your probleme, after you implement your code with the @writer decorator inside your model, you need to call for the collection and get your method via the modelClass property of the collection:

const createPost = async () => {
  // Instantiate your post collections first here
  const postCollection = database.collections.get<Post>('posts');

  // And now you can call for your methods via modelClass, don't forget to call it asynchronously
  const createPost = await postCollection.modedlClass.createPost(postData: PostType){
    // Write the rest of your code here
  };
}