A Netflix-like example app showcasing how to use different Realms in MongoDB's Atlas Device SDK for React Native. All users can browse (not play) movies from MongoDB's Mflix sample dataset, but only users who register with email and password are able to sync, read, add, and remove movies saved to "My List".
This example app does not support playing any movies.
The following shows the project structure and the most relevant files.
To learn more about the backend file structure, see App Configuration.
├── backend - App Services App
│ └── (see link above)
│
├── frontend - React Native App
│ ├── app
│ │ ├── atlas-app-services
│ │ │ └── config.ts - Add App ID
│ │ │
│ │ ├── components
│ │ │ ├── MovieItem.tsx - Movie list item
│ │ │ ├── MovieList.tsx - Horizontal movie list
│ │ │ └── PublicLogin.tsx - Logs in public/anonymous users
│ │ │
│ │ ├── hooks
│ │ │ └── useAccountInfo.ts - Provides account info
│ │ │
│ │ ├── models
│ │ │ ├── Movie.ts - Mflix movie data model
│ │ │ └── PrivateContent.ts - Data for private users
│ │ │
│ │ ├── navigation
│ │ │ ├── MoviesNavigator.tsx - Navigates movie screens
│ │ │ ├── RootNavigator.tsx - Navigates bottom tabs
│ │ │ └── routes.ts - Available routes
│ │ │
│ │ ├── providers
│ │ │ └── MovieProvider.tsx - Queries and updates data
│ │ │
│ │ ├── screens
│ │ │ ├── AccountScreen.tsx - Login and account info
│ │ │ ├── MovieInfoScreen.tsx - Movie info and add to My List
│ │ │ └── MoviesScreen.tsx - Movies grouped by category
│ │ │
│ │ ├── App.tsx - Provides the App Services App
│ │ └── AuthenticatedApp.tsx - Opens different Realms
│ │
│ ├── index.js - Entry point
│ └── package.json - Dependencies
│
└── README.md - Instructions and info
This app focuses on showing how multiple Realms can be used. The AuthenticatedApp
component renders @realm/react's RealmProvider
available for opening a Realm and providing it to the child components in its React subtree. It will open different Realms (but only one is kept open at any given time in this app) depending on whether or not the account is "public/anonymous". Users that log in using email and password will be treated as private accounts with the ability to manage "My List".
It specifically addresses the following points:
- Using a "public" Realm and automatically logging in users using Anonymous Authentication.
- Automatic login occurs in order to sync all public content (the Mflix dataset) to all users.
- Using a "private" Realm and logging in using Email/Password Authentication.
- These users use a different Realm to additionally be able to sync, read, add, and remove movies saved to "My List".
- Configurations for opening a synced Realm.
- Initial subscriptions are added to allow syncing of a subset of data to the device.
- Realms are opened immediately without waiting for downloads from the server.
- See Offline Support further below.
- Using an Atlas Authentication Trigger and Atlas Function.
- The trigger is triggered when users register with email and password.
- The trigger in turn calls a function to create a
PrivateContent
document containing themyList
field.
- Using different data access rules/permissions for the "public" and "private" Realms.
- See Set Data Access Permissions further below.
This app uses multiple Realms, but only one Realm is kept open at any given time as only one RealmProvider
is used, but with different configurations. You can also have RealmProvider
s in different parts of the React tree, specifying different path
s in order to open multiple Realms at the same time.
This app uses synced Realms as it needs to load the Mflix dataset from Atlas. Thus, for logging in, a network connection is required.
Users who have logged in at least once will have their credentials cached on the client. Thus, a logged in user who restarts the app will remain logged in. @realm/react's UserProvider
automatically handles this for you by checking if the app.currentUser
already exists.
Data that was previously synced to the device will also exist locally in the Realm database. From this point on, users can be offline and still query and update data. Any changes made offline will be synced automatically to Atlas and any other devices once a network connection is established. If multiple users modify the same data either while online or offline, those conflicts are automatically resolved before being synced.
When opening a Realm, we can specify the behavior in the Realm configuration when opening it for the first time (via newRealmFileBehavior
) and for subsequent ones (via existingRealmFileBehavior
). We can either:
OpenRealmBehaviorType.OpenImmediately
- Opens the Realm file immediately if it exists, otherwise it first creates a new empty Realm file then opens it.
- This lets users use the app with the existing data, while syncing any changes to the device in the background.
OpenRealmBehaviorType.DownloadBeforeOpen
- If there is data to be downloaded, this waits for the data to be fully synced before opening the Realm.
This app opens a Realm via RealmProvider
(see AuthenticatedApp.tsx) and passes the configuration as props. We use OpenImmediately
for new and existing Realm files in order to use the app while offline.
See OpenRealmBehaviorConfiguration for possible configurations of new and existing Realm file behaviors.
Each movie poster is loaded from a remote source and is not cached on the client for this example app. Therefore, if the user is offline, only the placeholder image will be shown as the poster.
- Node.js
- React Native development environment
- Refer to the "React Native CLI Quickstart".
- Deploy a free Atlas cluster and create an Atlas database.
- Load the Sample Mflix Dataset into your Atlas database.
- Several databases and collections exist in the sample dataset, but we will only be using the
sample_mflix
database and itsmovies
collection.
- Several databases and collections exist in the sample dataset, but we will only be using the
You can either choose to set up your App via a CLI (this has fewer steps and is much faster since all configurations are already provided in the backend directory), or via the App Services UI (steps provided below).
To import and deploy changes from your local directory to App Services you can use the command line interface:
- Set up Realm CLI.
- In the provided backend directory (the App Services App), update the following:
- Cluster Name
- Update the
"clusterName"
in data_sources/mongodb-atlas/config.json to the name of your cluster. - (The default name is
Cluster0
.)
- Update the
- App ID
- There is no
"app_id"
defined in realm_config.json since we will create a brand new App. If you for some reason are updating an existing app, add an"app_id"
field and its value.
- There is no
- Cluster Name
- Push and deploy the local directory to App Services:
realm-cli push --local <path to backend directory>
- Once pushed, verify that your App shows up in the App Services UI and that both triggers have the status
Enabled
. - 🥳 You can now go ahead and install dependencies and run the React Native app.
To sync data used in this app you must first:
- Create an App Services App.
- Enable Email/Password Authentication and Anonymous Authentication.
- For this example app, we automatically confirm users' emails.
- Enable Flexible Sync with Development Mode enabled.
- Select
sample_mflix
as the database to store new collections in. - When Development Mode is enabled, queryable fields will be added automatically, and schemas will be inferred based on the client Realm data models.
- For information, queryable fields used in this app include:
- Global (all collections):
_id
movies
collection:title
,fullplot
,type
,year
,genres
,poster
PrivateContent
collection:userId
- Global (all collections):
- (Development Mode should be turned off in production.)
- Select
- Don't forget to click
Review Draft and Deploy
.
If you set up your App Services App via a CLI, you can skip this step as the triggers and functions should already be defined for you.
We will add a trigger that is automatically triggered when users register with email and password. This trigger in turn calls a function to create a PrivateContent
document containing the myList
field. When a user is deleted (e.g. by manually deleting them via the App Services UI) we also add a trigger and function for deleting the PrivateContent
document.
To set this up via the App Services UI:
- Define two functions with the following configurations:
- Function name:
createPrivateContent
- Authentication:
Application Authentication
- Private:
true
- Code: See backend function
- Authentication:
- Function name:
deletePrivateContent
- Authentication:
Application Authentication
- Private:
true
- Code: See backend function
- Authentication:
- Function name:
- Define two triggers with the following configurations:
- Trigger name:
onNewUser
- Trigger type:
Authentication
- Enabled:
true
- Action type:
Create
- Providers:
Email/Password
- Event type:
Function
- Function:
createPrivateContent
- Trigger type:
- Trigger name:
onDeletedUser
- Trigger type:
Authentication
- Enabled:
true
- Action type:
Delete
- Providers:
Email/Password
- Event type:
Function
- Function:
deletePrivateContent
- Trigger type:
- Trigger name:
From the frontend directory, run:
npm install
If developing with iOS, also run:
npx pod-install
- Copy your Atlas App ID from the App Services UI.
- Paste the copied ID as the value of the existing variable
ATLAS_APP_ID
in app/atlas-app-services/config.ts:
import Config from 'react-native-config';
export const ATLAS_APP_ID = Config.ATLAS_APP_ID || 'YOUR_APP_ID';
- Alternatively you can add a
.env
file to your project with the following contents:This file is included in theATLAS_APP_ID=your_app_id
.gitignore
so that it won't be committed to any code repositories.
- Start Metro (the JavaScript bundler) in its own terminal:
npm start
- In another terminal, start the app:
# Open the app on an iOS simulator.
npm run ios
# Open the app on an Android emulator.
npm run android
To run the app on an actual device, see React Native's Running on Device.
If you set up your App Services App via a CLI, you can skip this step as the permissions should already be defined for you.
After running the client app for the first time, modify the rules for the collections in the App Services UI for increased security.
- Collection:
movies
- Permissions:
readAll
(see corresponding json) - Explanation:
- All users will be able to browse the movies on their device, but they should not be able to update, insert, or delete any movies.
- Permissions:
- Collection:
PrivateContent
- Permissions:
readOwnWriteOwn
(see corresponding json) - Explanation:
- Users who have registered with email and password each have a
PrivateContent
document. A user will be able to read and write to their own document (i.e. whenPrivateContent.userId === <App User ID>
), but not anyone else's. Therefore, we also do not allow the client to modify theuserId
field.
- Users who have registered with email and password each have a
- Permissions:
To learn more and see examples of permissions depending on a certain use case, see Device Sync Permissions Guide and Data Access Role Examples.
A great help when troubleshooting is to look at the Application Logs in the App Services UI.
If permission is denied:
- Make sure your IP address is on the IP Access List for your App.
- Make sure you have the correct data access permissions for the collections.
- See Set Data Access Permissions further above.
Removing the local database can be useful for certain errors.
On an iOS simulator:
- Press and hold the app icon on the Home Screen.
- Choose to remove the app and its data.
On an Android emulator via Android Studio:
- Quit the emulator if it is running.
- Open
Device Manager
. - Select
Wipe Data
for the relevant emulator.