Build offline-first mobile application using MongoDB Realm Sync in React Native
Lakshman Kambam
Full-Stack Developer ? Technical Project Manager ? Cloud Architect ? JavaScript ? React.js ? Next.js ? TypeScript ? Node.js ? Java Spring Boot ? MySQL ? MongoDB ? AWS EC2 ? Google Cloud Digital Leader (Certified)
What are offline-first Applications?
“If you’re not familiar with the term “Offline First”, it refers to applications designed to work without an internet connection. While these applications might make network requests when available, they can still operate when the internet is disconnected or flaky.”
All you need to know about Realm:
Realm’s mobile database is an open source, developer-friendly alternative to CoreData and SQLite. Start in minutes, port your app in hours, and save yourself weeks of work.
MongoDB acquired Realm, an open-source database geared for mobile applications, for $39 million. The startup had raised just over $40 million before being acquired on June 2020.
Realm has more than 100,000 developers using its product, with 350 companies using its data synchronisation tools to move data between mobile devices and the cloud, using the Realm Platform, according to the company.
MongoDB Realm:
MongoDB and Realm are fully committed to investing in the Realm Database and the future of data synchronisation, and taking both to the next phase of their evolution
MongoDB Realm enables sync between Realm Database and MongoDB Atlas, seamlessly stitching together the two components into an application layer for your mobile app.
MongoDB Atlas provides cloud-hosted managed instances of MongoDB are always available.
1. Realm Users & Authentication
- it has built-in user mgt with easy integration into third-party authentication providers.
- like Facebook, Google, and Apple.
2. Realm Functions
- You can call pre defined Realm Functions from a module of the Realm SDK in your client application.
- Like endpoints in a REST API
3. Realm Triggers
- Automatically execute a function at a scheduled time or when an event occurs.
4. Realm Rules
- Access data based on roles managed by you.
5. Supports GraphQL API to access data.
- MongoDB Realm calculates usage based on the data you sync and the requests your application makes.
MongoDB Realm calculates usage based on the data you sync and the requests your application makes.
You’ll only pay for what you need. (Sync hours with requests).
"Note: focusing more on functionality. Keeping it very simple about creating MongoDB Realm UI Steps which are easy. i’ve mentioned official docs links below for all get started steps."
In this tutorial, you’re going to learn how to build MongoDB Realm Sync Online or Offline in React Native.
This tutorial will cover every step in detail and include code snippets for each. you’ll work through this tutorial by creating a new react-native demo project.
It support Realm Sync with offline means stores data in local and will sync once online. Sync configuration need to be switched based on network connectivity status.
"Note: demo app supports offline sync, Open & Close a Local Realm — React Native SDK. To open a local (non-synced) realm, pass a Realm.Configuration object to Realm.open()"
I've created working example of Books CRUD demo app (MongoDB Realm Sync with online). please follow below steps to make your demo app.
Let’s get started with MongoDB Realm, React Native SDK for TypeScript and JavaScript.
Step 1: Create a React Native Project and Package.json Setup
react-native init MyRealmApp
Next, Install required packages for this project
npm install --save packageName
Package.json dependencies
"dependencies": { "bson": "^4.4.0", "prop-types": "^15.7.2", "react": "17.0.1", "react-native": "0.64.1", "react-native-get-random-values": "^1.7.0", "react-native-vector-icons": "^8.1.0", "realm": "^10.4.1" }
- bson — to generate mongodb like ObjectId().
- prop-types — you can use prop-types to document the intended types of properties passed to components.
- react-native-get-random-values — to avoid Error: crypto.getRandomValues() not supported in react native.
- react-native-vector-icons — customizable icons for UI.
- realm — realm react native SDK. (Installation Guide in Step 3)
Step 2: Folder Structure and Clean up
Next, you need to create a folder structure as shown in the screenshot below:
You need to clean up App.js file as shown below:
import React from 'react'; import {SafeAreaView} from 'react-native'; import BookStore from './src'; const App = () => { return ( <SafeAreaView style={{flex:1}}> <BookStore /> </SafeAreaView> ); } export default App;
Step 3: Realm Configuration & Define Object Model Schema
Install Realm with npm
npm install --save realm
Resolve CocoaPods Dependencies
For the iOS app, fetch the CocoaPods dependencies with the following commands from your React Native project directory:
cd ios && pod install && cd ..
Inside database directory, create files as shown in screenshot below:
I’ve created custom method to generate MongoDB like ObjectId but i never used it. so nvm ObjectId.js
RealmConfig.js will look like this:
import Realm from 'realm'; import { BookSchema } from '../schemas'; // place your RealmApp ID here const app = new Realm.App({ id: "REALM-APP-ID", timeout: 10000 }); // can implement inBuilt JWT, Google, Facebook, Apple Authentication Flow. const credentials = Realm.Credentials.anonymous(); // LoggingIn as Anonymous User. getRealm = async () => { // loggedIn as anonymous user const loggedInUser = await app.logIn(credentials); // MongoDB RealmConfiguration const configuration = { schema: [BookSchema], // add multiple schemas, comma seperated. sync: { user: app.currentUser, // loggedIn User partitionValue: "2F6092d4c594587f582ef165a0", // should be userId(Unique) so it can manage particular user related documents in DB by userId } }; return Realm.open(configuration); } export default getRealm;
As i mentioned earlier, please refer to MongoDB Realm documentation to Create a Realm App (Realm UI)
"Note: Replace RealmAppId and partitionValue with yours."
Now that you have created the realm app instance and configuration, the next step is to define your object model for the data that you’re planning to sync.
Define Your Object Model
If have not enabled Realm Sync or you enabled Sync with development mode in the Realm UI, you can define your object model directly in code.
Inside schemas directory, create BookSchema as shown in screenshot below:
BookSchema.js will look like as shown in the snippet below:
const BookSchema = { name: 'Book', properties: { _id: 'objectId', author: 'string?', category: 'string?', price: 'string?', realm_id: 'string?', // should be userId or add any static for test project. title: 'string', }, primaryKey: '_id', }; export default BookSchema;
you can define BookSchema.js in RealmConfig.js configuration like this
const configuration = { schema: [BookSchema], // add multiple schemas, comma seperated. sync: { user: app.currentUser, // loggedIn User partitionValue: "2F6092d4c594587f582ef165a0" //singleUserId } } };
Partitioning helps you turn the large amount of data stored by your application into the small amount of data needed by individual clients, called realms.
Client applications only need the data relevant to the current user. Most users only have permission to access a limited subset of the data in a linked cluster. Some data, like user settings and personal information, are only relevant to a single user.
Know more about PartitionValue Partition Atlas Data into Realms.
Step 4: Authenticate a User & Open a Realm once Enabled Realm Sync
When you have enabled anonymous authentication in the Realm UI, users can immediately log into your app without providing any identifying information:
Note: Potential use cases for anonymous authentication include, Allowing end users to try the features of an application before registering for an account.
Authentication Providers
Realm provides many additional ways to authenticate, register, and link users. Example: Email/Password, Apple, Google, Facebook etc.,
Checkout the list of the Authentication Providers available in Realm.
Now, we login as anonymous for demo purpose. we’ve already created an anonymous credential in RealmConfig.js
..... // Place Your RealmApp ID Here const app = new Realm.App({ id: "REALM-APP-ID", timeout: 10000 }); // can implement inBuilt JWT, Google, Facebook, Apple Authentication Flow. const credentials = Realm.Credentials.anonymous(); // LoggingIn as Anonymous User. getRealm = async () => { // loggedIn as anonymous user const loggedInUser = await app.logIn(credentials); .....
Once we’ve have enabled Realm Sync and authenticated a user, you can open a synced realm:
Now that we’ve understood realm instance and configuration. we’ll get right into Realm Open.
Open a Synced Realm
To open a synced realm, call Realm.open(). Pass in a Configuration object, which must include the sync property defining a SyncConfiguration object.
Realm.open(configuration);
In the following example, a synced realm is opened with a schema value of a predefined BookSchema SyncConfiguration object that uses the currently logged in user and a partition value of 2f6092d4c594587f582ef1
if you add partitionKey as realm_id from BookSchema it will sync all books data that match realm_id=6092d4c594587f582ef1
JSON Schema
Unlike regular Realm objects, which map to their own MongoDB collection, embedded objects map to embedded documents in the parent type’s document schema:
Document Schema Configuration
Documents in MongoDB are objects stored in a format called BSON, a binary-encoded superset of JSON that supports additional data types. The root of every document schema in Realm is a BSON Object schema that applies to each document in a collection.
{ "bsonType": "object", "title": "<Type Name>", "required": ["<Required Field Name>", ...], "properties": { "<Field Name>": <Schema Document> } } }
Book Document Schema Screenshot
Now that you have opened a realm,
In the next step, you will learn to modify it and its objects in a write transaction block.
Step 5: Create, Read, Update, and Delete Book Document Objects
To create a new Book run the method “realm.create()” inside the “realm.write()” callback. Pass the string “Book” as the first parameter to “realm.create()”. Pass a book data object as the second parameter.
I’ve created few UI Screens for CRUD operations to make it understandable, a bit better.
I made a basic demo BookStore app UI Screen Flow.
BookStore UI Screen Components in demo project folder structure as shown in screenshot below:
Create or Update a Book
Now, Just import getRealm realm config method from RealmConfig.js
import getRealm from './database';
Create Book transaction code block example:
// get RealmApp Config import getRealm from './database'; ... // realm.write transaction example // Add Book - onSubmit method, realm.write(() => { realm.create('Book', { _id: ObjectId('60a5501a74ab1032ef086986'), author: 'me', category: 'Data', price: '0', realm_id: '2F6092d4c594587f582ef165a0', // should be same as partitionValue title: 'MongoDB Realm Sync Online and Offline DB Config', }); }); const booksListUpdated = realm.objects('Book'); // getLatestUpdateds Books data Objects setBooks([...booksListUpdated]); // setBooks in localState ...
Update/modify a book, update its properties in a write transaction. similar to create but the only difference is that you need to pass 3rd argument as string “modified” like below:
realm.create('Book', editingBookObj, 'modified');
here, it checks if data object already exists with given _id of book that you’re trying to edit.
Read/Get Books, you can retrieve a live collection of all books in the realm:
"Book" is a collection name.
realm.objects("Book");
You can also filter that collection using a filter:
const books = realm.objects("Book"); const books = realm.objects("Book"); let filteredBooks = books.filtered("category = 'Science');
Delete a Book, from the realm with realm.delete
// get RealmApp Config import getRealm from './database'; ... const deleteBook = async (bookId) => { try { const realm = await getRealm(); // Searches for a Realm object by its primary key. const deleteBook = realm.objectForPrimaryKey('Book', bookId); realm.write(() => { if(deleteBook) { realm.delete(deleteBook); // realm.delete: to delete the book from the realm. } }); const results = realm.objects('Book'); // retrieve live objects list from realm setBooks(results); // setBook in localState } catch(error) { if(error){ console.log(error.message); } } } ...
“objectForPrimaryKey” — Searches for a Realm object by its primary key.
you’ve reached end of this tutorial on building offline-first mobile application using MongoDB Realm Sync in React Native.
We have our final output:
The complete demo source code for this tutorial is available in the Github Repository. Check it!
also, you can raise an issue if you’re stuck.
Happy Coding!
this is my first article in LinkedIn, i found few bugs while writing this article. mentioned few of them below:
- codeSnippet format.
- image alignment.
- line break issues.
I request LinkedIn to improve article editor UI and Functionality so it will be easy and fast for LinkedIn community, who want to share their ideas, thoughts and work demos through articles.
Thank you for reading and if this post was helpful, follow me on LinkedIn to stay up to date with my upcoming posts!
About me: https://lakshmankambam.com
GitHub: https://github.com/klakshman318
StackOverflow: https://stackoverflow.com/users/1814229/lakshman-kambam
UpLabs: https://www.uplabs.com/klakshman318
Behance: https://www.behance.net/lakshmanweb
Medium Article: https://lakshmankambam.medium.com/build-offline-first-mobile-application-using-mongodb-realm-sync-in-react-native-eac69ece4928
Senior .Net Developer at FlairsTech
1 年nice, but things have changed since then ?? JavaScript!
Software Engineer/Developer | C# .NET JavaScript
2 年Hello,cool
Arquiteto de front end na WEG | MBA em Ciência da Computa??o
3 年Good job :D