React Native with WatermelonDB: A Lightweight and Reactive Database for Scalable Apps
Sachin Gaggar
Posted on September 27, 2024
When it comes to building scalable React Native applications, WatermelonDB is an excellent lightweight database solution. Adding only about 2MB to your app's size, it offers excellent performance, real-time reactivity, and allows you to manage complex relationships between data models efficiently. With WatermelonDB, you can easily handle operations like creating, reading, updating, and deleting records, and manage relationships using decorators like @children and @relation. It also provides you with sync functionality to remote databse.
In this blog, we'll cover how to set up WatermelonDB, define more complex models with relationships, and perform CRUD operations, including setting children and related records.
Setting Up WatermelonDB in React Native
1. Installation
Start by installing WatermelonDB in your React Native project:
npm install @nozbe/watermelondb
npm install @nozbe/watermelondb/native --save
npx pod-install
2. Define the Schema
Define the schema for your database. We’ll add some extra fields like age
for users
and content
for posts, plus we’ll establish a relationship where users
have multiple posts
.
// schema.js
import { tableSchema } from '@nozbe/watermelondb/Schema';
export const mySchema = {
version: 1,
tables: [
tableSchema({
name: 'users',
columns: [
{ name: 'name', type: 'string' },
{ name: 'email', type: 'string' }, // New field for email
{ name: 'age', type: 'number' }, // New field for age
],
}),
tableSchema({
name: 'posts',
columns: [
{ name: 'user_id', type: 'string', isIndexed: true }, // Foreign key to users
{ name: 'title', type: 'string' },
{ name: 'content', type: 'string' }, // New field for content
{ name: 'created_at', type: 'number' }, // New timestamp field
],
}),
],
};
3. Define Models with Relationships
WatermelonDB uses decorators like @field, @children, and @relation to manage fields and relationships between models. Let’s define a User model that has many Post records, and a Post model that belongs to a User.
// User.js
import { Model } from '@nozbe/watermelondb';
import { text, field, date, children } from '@nozbe/watermelondb/decorators';
export default class User extends Model {
static table = 'users';
@text('name') name;
@text('email') email; // New field for email
@field('age') age; // New field for age
@children('posts') posts; // One-to-many relationship with posts
}
// Post.js
import { Model } from '@nozbe/watermelondb';
import { text, date, relation } from '@nozbe/watermelondb/decorators';
export default class Post extends Model {
static table = 'posts';
@text('title') title;
@text('content') content; // New field for content
@date('created_at') createdAt; // New timestamp field
@relation('users', 'user_id') user; // Relation to the users table
}
4. Set Up the Database
You now need to set up the database with the schema and models you defined:
// database.js
import { Database } from '@nozbe/watermelondb';
import SQLiteAdapter from '@nozbe/watermelondb/adapters/sqlite';
import { mySchema } from './schema';
import User from './User';
import Post from './Post';
const adapter = new SQLiteAdapter({
schema: mySchema,
});
export const database = new Database({
adapter,
modelClasses: [User, Post],
actionsEnabled: true,
});
CRUD Operations with WatermelonDB
Let’s walk through performing CRUD operations with WatermelonDB, including handling relations between users and posts.
1. Create a User and a Post
You can create new users and posts by using the create method inside a database.write block. Remember always write crud operation inside write block. Now here you have freedom either, we can create this method directly inside models or write a separate file.:
// Create a User
const addUser = async (name, email, age) => {
await database.write(async () => {
await database.collections.get('users').create(user => {
user.name = name;
user.email = email; // Setting email field
user.age = age; // Setting age field
});
});
};
// Create a Post for a User
const addPost = async (user, title, content) => {
await database.write(async () => {
await database.collections.get('posts').create(post => {
/**
* Setting relation to User. This is very very important.
* Without this set you can't take advantage of flexibilty provided by children decorator
**/
post.user.set(user);
post.title = title;
post.content = content; // Setting content field
post.createdAt = Date.now(); // Setting timestamp
});
});
};
2. Read Records
Fetching records is straightforward, and you can query related data like posts belonging to a user.
// Fetch all users
const fetchUsers = async () => {
const users = await database.collections.get('users').query().fetch();
return users;
};
// Fetch posts for a specific user
const fetchUserPosts = async (user) => {
const posts = await user.posts.fetch();
// Using @children decorator all posts will be collected
// related to this user
return posts;
};
// Fetch the user who owns a post
const fetchPostOwner = async (post) => {
const user = await post.user.fetch(); // Fetches the related user using @relation
return user;
};
3. Update Records
Updating records is done by fetching a record and modifying it within a database.write block:
// Update a User
const updateUser = async (user, newName, newAge) => {
await database.write(async () => {
await user.update(u => {
u.name = newName;
u.age = newAge;
});
});
};
// Update a Post
const updatePost = async (post, newTitle, newContent) => {
await database.write(async () => {
await post.update(p => {
p.title = newTitle;
p.content = newContent;
});
});
};
4. Delete Records
WatermelonDB allows both soft delete (marks as deleted) and hard delete (permanently removes the record):
// Soft delete a User
const softDeleteUser = async (user) => {
await database.write(async () => {
await user.markAsDeleted();
});
};
// Hard delete a User
const hardDeleteUser = async (user) => {
await database.write(async () => {
await user.destroyPermanently();
});
};
Conclusion
WatermelonDB is a lightweight, high-performance solution for React Native apps, adding only 2MB to your app size while providing efficient data handling for large datasets. By leveraging decorators like @children and @relation, you can easily define and manage relationships between records. The asynchronous CRUD operations ensure smooth UI performance, making it an ideal database choice for both small and large-scale applications.
Whether you're handling complex relationships or just starting with simple data, WatermelonDB’s combination of reactivity, performance, and ease of use makes it a great option for React Native developers.
Feel free to drop a comment if you have any questions. Also, take a look at withObservables, a Higher-Order Component (HOC) that automatically re-renders your UI whenever there are changes in the database values.
Posted on September 27, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
September 27, 2024