RMP Walkthrough
Realm Mobile Platform Overview
The core concept of the Realm Mobile Platform is a lightweight object container called a Realm. Like a database, data in Realms can be queried and filtered, interconnected, and persisted. Unlike a traditional database, though, objects in Realms are live and fully reactive. Realms synchronize seamlessly between devices and applications, and can be accessed safely across threads.
The Realm Mobile Database is built from the ground up to run on mobile devices. Unlike a traditional database, objects in a Realm are native objects. You don’t have to copy objects out of the database, modify them, and save them back—you’re always working with the “live,” real object. If one thread or process modifies an object, other threads and processes can be immediately notified. Objects always stay in sync.
Applications built on the Realm Mobile Platform have the ability to access and create Realms on the Realm Object Server. All your application needs is the connection information for your Object Server and the URLs of the server-side Realms. Objects on the Realm Object Server will be downloaded as the application needs them–then the local copy of the Realm will be kept fully synchronized with the Object Server’s copy.
A key concept of the Realm Mobile Platform is the use of multiple Realms: your application isn’t tied to a single data source, and data sources are as easy to access as web pages. Realms offer fine-grained access control tied to user accounts. A Realm on the Object Server can be public, accessible to all users (for instance, a catalog for an online store); private, accessible to only the user who created it (for instance, a private task list, or application settings); or shared between specific users (for instance, a household shopping list).
Installing Realm Developer Edition on AWS
We provide public Amazon Machine Images (AMIs) for the Realm Mobile Platform, using hardware virtual machine (HVM) virtualization based on Ubuntu 16.04. If you’re already running an Ubuntu, Red Hat Enterprise, or CentOS AMI, you can install Realm Developer Edition from Realm’s PackageCloud repository.
Installing the Developer Edition AMI
From the EC2 console, click AMIs under Images in the sidebar, and search for one of the following images based on your preferred region.
Region | Image AMI ID |
---|---|
ap-northeast-1 (Tokyo) | ami-a03233c7 |
eu-west-1 (Ireland) | ami-aecfdec8 |
us-east-1 (N. Virginia) | ami-8a62289c |
Select the image and lick Launch. On the next screen, “Choose an Instance Type,” select an instance type. You can leave the default, t2.micro
, selected to stay on the free tier, although we recommend m3.medium
. Click Review and Launch.
Finally, click Launch. You’ll be prompted to select an existing public/private key pair for SSH access or to create a new one. After the instance is launched, click View Instance. The dashboard will now show your running instance with its public IP address.
While the Realm instance has port 22 (SSH) open by default, it does not have Object Server-specific ports open. You’ll definitely need to open port 9080 (Realm Dashboard and client access), and may also need 7800 (sync workers), 27810 (backup workers), and if if you’re using statsd
for monitoring, 8125. Click on the instance’s security group and click on Edit inbound rules. Add a Custom TCP rule for port 9080
, allowing 0.0.0.0/0
for all IPs, or alternatively an IP address range to limit connections to. You can add other rules for other important ports, or you can specify a range of ports in one rule (e.g., 22-9999
), although doing so is less secure than opening only specific ports.
Once this rule is in place, you should be able to visit <ip-address>:9080
in your browser and launch the Realm Dashboard.
Installing on an Existing Linux AMI Instance
Realm maintains package repositories through a service called PackageCloud; installing and starting Realm Object Server takes just a few steps.
-
SSH into your existing instance.
-
Install the Object Server from Realm’s PackageCloud repository. On Ubuntu, type:
curl -s https://packagecloud.io/install/repositories/realm/realm/script.deb.sh | sudo bash sudo apt-get update sudo apt-get install realm-object-server-developer
On RHEL/Centos, type:
curl -s https://packagecloud.io/install/repositories/realm/realm/script.rpm.sh | sudo bash sudo yum -y install realm-object-server-developer
-
Start the Realm Object Server. On RHEL/Centos version 6, type:
sudo chkconfig realm-object-server on sudo service realm-object-server start
On Ubuntu or RHEL/Centos version 7, type:
sudo systemctl enable realm-object-server sudo systemctl start realm-object-server
Lastly, you’ll need to make sure that port 9080
is open on your AMI instance, as this is used for Realm clients and the Dashboard. You may also need 7800 (sync workers), 27810 (backup workers), and if if you’re using statsd
for monitoring, 8125. Click on the instance’s security group and click on Edit inbound rules. Add a Custom TCP rule for port 9080
, allowing 0.0.0.0/0
for all IPs, or alternatively an IP address range to limit connections to. You can add other rules for other important ports, or you can specify a range of ports in one rule (e.g., 22-9999
), although doing so is less secure than opening only specific ports.
Once this port is open, you should be able to visit <ip-address>:9080
in your browser and launch the Realm Dashboard.
The Realm Dashboard
Once your AMI is set up, visit the Dashboard in your browser (at <ip-address>:9080
). On your first access, you’ll be prompted to create an admin user by supplying an email address and password.
The Dashboard gives you the following features:
- Dashboard: System status, including real-time displays of data rate in/out, bytes in/out, open connections, and open Realms
- Realms: Paths, permissions, and owners of Realms synced to this Object Server, with the ability to “drill down” and display the models and contents of individual Realms
- Users: User information and management for this Object Server, including granting and removing administrative privileges
- Functions: Management and entry of Realm Functions
- Logs: System logs for the Object Server, with selectable detail level
Building an iOS App with RealmLoginKit
The Multi-User Tasks Tutorial walks you through building a multi-user version of RealmTasks, our canonical demo app. The tutorial will show you how to:
- Setup a new Realm-based project from scratch using Cocoapods
- How to adopt and setup a free Realm utility module called
RealmLoginKit
which allows you to easily created multi-user ready applications with almost zero coding - How to create a simple Realm-based Task Manager that can interoperate with the fully-featured RealmTasks that comes with the Realm Mobile Platform distribution
In order to successfully complete the tutorial, you will need a Macintosh running macOS 10.12 or later, as well as a copy of Xcode 8.2.3 or later.
For full instructions and source code, go to our GitHub repository for the tutorial:
https://github.com/realm-demos/realm-MultiUserTasksTutorial
Reactive Server-Side Features With Realm Functions
Now that you’ve got the Realm Object Server set up, we can start writing custom server-side features with Realm Functions.
Adding Your First Function
First, open up your web dashboard by typing in the IP address you used above. Then click on the Functions button on the left. That’ll show you a list of all your Functions. Let’s make a new one! Press “Create new Function,” and you’ll see the screen below:
Here, the first field is for the title of the Function—think of it as a filename that’ll help you differentiate between the different Functions you’ll create.
Next is a field to specify which Realms to monitor for changes. Because Realms are organized in a filesystem-like structure, we write this as a path. This field also supports regular expressions, so that you can get a Function to observe changes over a range of many Realms.
The regex we want for our Function is:
^/([0-9a-f]+)/realmtasks$
This watches for changes in any user subdirectories named realmtasks
. The first regex pattern — ([0-9a-f]+)
— is a way to look for directories belonging to specific users, which are named after their UUID. By using this pattern, we can watch over all the realmtasks
Realms, even if they all are scoped to specific users.
Once you’ve pasted that regex pattern, you can paste in the Functions JavaScript code that will be run in response to each new change event generated in those Realms. Copy and paste the following code into the code editor:
var Wit = require("node-wit").Wit;
var WIT_ACCESS_TOKEN = ""; // UPDATE WITH API TOKEN
var witClient = new Wit({accessToken: WIT_ACCESS_TOKEN});
module.exports = function(change_event) {
var realm = change_event.realm;
var changes = change_event.changes.Task;
var taskIndexes = changes.modifications;
console.log("Change detected: " + changes);
// Get the task objects to processes
var tasks = realm.objects("Task");
for (var i = 0; i < taskIndexes.length; i++) {
var taskIndex = taskIndexes[i];
// Retrieve the task object from the Realm with index
var task = tasks[taskIndex];
console.log("New task received: " + change_event.path);
// get date from wit.ai
// node-wit: https://github.com/wit-ai/node-wit
witClient.message(task.text, {}).then((data) => {
console.log("Response received from wit: " + JSON.stringify(data));
var dateTime = data.entities.datetime[0];
if (!dateTime) {
console.log("Couldn't find a date.");
return;
}
console.log("Isolated calculated date: " + dateTime.value);
// to write the date, we'll have to add a date property on the client and migrate it
realm.write(function() {
task.date = new Date(dateTime.value);
});
})
.catch(console.error);
}
};
The structure of this code is pretty straightforward. Whenever one or a series of changes in the observed Realms is synced to the Realm Object Server, this Function receives a changeEvent object. It will contain the indices of any of the objects that changed — and specifies whether they were created, updated, or deleted. However, since it just contains the indices of the changed objects and not the objects themselves, you have to retrieve the objects themselves:
...
var realm = change_event.realm;
var changes = change_event.changes.Task; // changes just for the Task model
var taskIndexes = changes.modifications; // we’re looking for updates, not creations or deletes
// Get the task objects to processes
var tasks = realm.objects("Task"); // this is the list of ALL objects of that model type
for (var i = 0; i < taskIndexes.length; i++) {
var taskIndex = taskIndexes[i]; // here we get the current index
// Retrieve the task object from the Realm with index
var task = tasks[taskIndex]; // finally, we retrieve the task object from the Realm
}
...
Now, before this code will run, you’ll have to follow the instructions beginning here to get your Wit.ai access token, as well as configure your Wit.ai instance to watch for datetimes embedded in the text of the Task object. Once you finish step 3 at that link, you can just paste in the access token at the top of your Function, in the WIT_ACCESS_TOKEN
variable.
Finally, we have to install the node-wit
package. Open your shell and add your Realm Mobile Platform folder to your path:
cd ENTER/THE/PATH/TO/YOUR/RMP/FOLDER
PATH=.prefix/bin:$PATH npm install node-wit
With your access token in place and the package installed, it’s time to save and run the function. Press the ‘play’ button in the top right, and you’ll see the Enabled
indicator pop up on the left:
To see what’s happening in the Function and to debug it, you can scroll down this page and see the console.
Now you’re ready to turn on your iPhone app again, add a task like “Buy groceries at 5pm tomorrow” or “Go for a run in 20 minutes,” and see as it’s processed by the Function, logged in the console, and resolved into a reminder
property that updates automatically in the app, below the text of the task.
That’s it! You’ve written your first Function. Maybe your next Function might schedule Push Notifications to these tasks, based on the reminder
property we’ve filled in here? You can really build as many server-side features as you’d like, just by writing JavaScript.
Upgrading to Professional Edition
First, you’ll have to uninstall the existing edition you have installed. For platform-specific upgrade instructions to Professional Edition, click here.
Why upgrade? While Realm Functions is available to all users of the Realm Mobile Platform, your PE or EE license also enables you to use the Realm Sync binding for Node.js outside of the Realm Object Server, enabling the Event Handling, Data Access and Data Connector features.
You could use it to enable distributed event handling that responds to data changes in a more performant way, or you could use it to interact with the Realm Object Server from your existing servers, enabling you to push data to the Realm Object Server instead of simply reacting to data that arrives on the Realm Object Server.
To enable the Event Handling, Data Access, and Data Connector features that the Node.js binding makes possible, you must enable these APIs by including your access token in your Node.js application. (This token is sent to you via email when you sign up for a trial or purchase the Professional or Enterprise editions.) Near the start of your Node.js application, include your token and make a setAccessToken call to the Object Server:
var token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
Realm.Sync.setAccessToken(token);
Alternatively, you could load the token from disk:
var fs = require('fs')
RealmSync.setAccessToken(fs.readFileSync('./access-token', 'utf-8'));
Create an Event Handler
To use Realm Event Handling, you’ll need to create a small Node.js application.
Create a directory to place the server files, then create a file named package.json
. This JSON file is used by Node.js and NPM, its package manager, to describe an application and specify external dependencies.
You can create this file interactively by using npm init
. You can also fill in a simple skeleton file yourself using your text editor:
{
"name": "MyApp",
"version": "0.0.1",
"main": "index.js",
"author": "Your Name",
"description": "My Cool Realm App",
"dependencies": {
"realm": "file:realm-1.0.0-professional.tgz"
}
}
We specify the downloaded archive of the Realm Mobile Platform as a file: dependency; that archive should be in the same directory as the package.json file. (Make sure the filename specified in package.json is the filename that’s actually on disk!)
If you have other dependencies for your application, specify them in the dependencies section of this file.
After the package.json file is configured properly, type:
npm install
to download, unpack and configure all the modules and their dependencies.
Your event handler will need to access the Object Server with administrative privileges, so you’ll need to get the Object Server’s admin token. Under Linux, view the token with:
cat /etc/realm/admin_token.base64
On macOS, the token is stored in the realm-object-server folder, inside the Realm Mobile Platform folder. Navigate to that folder and view the token:
cd path-to/realm-mobile-platform
cat realm-object-server/admin_token.base64
Now that you’ve set up everything you need, you can create the file where the event handling code lives. A sample index.js file might look something like this:
var Realm = require('realm');
var ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
// Unlock Professional Edition APIs
Realm.Sync.setAccessToken(ACCESS_TOKEN);
// Insert the Realm admin token here
// Linux: cat /etc/realm/admin_token.base64
// macOS: cat realm-object-server/admin_token.base64
var ADMIN_TOKEN = 'ADMIN_TOKEN';
// the URL to the Realm Object Server
var SERVER_URL = 'realm://127.0.0.1:9080';
// The regular expression you provide restricts the observed Realm files to only the subset you
// are actually interested in. This is done in a separate step to avoid the cost
// of computing the fine-grained change set if it's not necessary.
var NOTIFIER_PATH = '^/.*/private$';
// The handleChange callback is called for every observed Realm file whenever it
// has changes. It is called with a change event which contains the path, the Realm,
// a version of the Realm from before the change, and indexes indication all objects
// which were added, deleted, or modified in this change
function handleChange(changeEvent) {
// Extract the user ID from the virtual path, assuming that we're using
// a filter which only subscribes us to updates of user-scoped Realms.
var matches = changeEvent.path.match(/^\/([0-9a-f]+)\/private$/);
var userId = matches[1];
var realm = changeEvent.realm;
var coupons = realm.objects('Coupon');
var couponIndexes = changeEvent.changes.Coupon.insertions;
for (var couponIndex of couponIndexes) {
var coupon = coupons[couponIndex];
if (coupon.isValid !== undefined) {
var isValid = verifyCouponForUser(coupon, userId);
// Attention: Writes here will trigger a subsequent notification.
// Take care that this doesn't cause infinite changes!
realm.write(function() {
coupon.isValid = isValid;
});
}
}
}
// create the admin user
var adminUser = Realm.Sync.User.adminUser(adminToken);
// register the event handler callback
Realm.Sync.addListener(SERVER_URL, adminUser, NOTIFIER_PATH, 'change', handleChange);
console.log('Listening for Realm changes');
This example listens for changes to a user-specific private Realm at the virtual path /~/private
(the tilde represents a wildcard). It will look for updated Coupon objects in these Realms, verify their coupon code if it wasn’t verified yet, and write the result of the verification into the isValid property of the Coupon object.
Upgrading to the Enterprise Edition
To upgrade, you’ll need to get an access token from Realm from email. First you’ll stop and uninstall the Realm Object Server Professional Edition, keeping the data in the Realms intact, then install the Enterprise Edition. This work can be done from the Unix shell prompt on your server.
SSH to the server running Realm Object Server, and type the following commands. (Lines beginning with #
are comments and don’t need to be entered.)
# Stop the service before uninstalling
sudo systemctl stop realm-object-server
# Uninstall the Professional Edition
sudo apt-get remove realm-object-server-professional
# Set up your new Enterprise Edition token
export PACKAGECLOUD_TOKEN=<the token you received via email>
# Set up the new package repository
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update
# Install the Realm Object Server
sudo apt-get install realm-object-server-enterprise
# Enable and start the service
sudo systemctl enable realm-object-server
sudo systemctl start realm-object-server
You should be able to browse to the same IP address and port and view the Realm Object Server dashboard using the same admin account and view the Realms you created previously in this walkthrough.
Adding Sync Workers
The sync worker is the process that stores Realms, responds to Realm mobile clients, and merges changes with Realm’s conflict resolution algorithm. The Enterprise Edition of the Realm Object Server comes with a sync worker installed on the server in the default configuration file. In some cases you may want to separate the sync workers from the Realm Object Server, which handles the authentication and routing of incoming realm mobile client requests, for security, fault tolerance, and load separation concerns. Let’s add an additional sync worker to our architecture.
The first thing you need to do is spin up a new EC2 instance for your new sync worker. Follow the instructions at the beginning of this walkthrough and SSH to the new instance, then install the realm-sync-worker
package from Packagecloud.
# Set up the Enterprise Edition token
export PACKAGECLOUD_TOKEN=<the token you received via email>
# Set up the new package repository
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update
# Install the Sync Worker
sudo apt-get install realm-sync-worker
# Enable and start the service
sudo systemctl enable realm-sync-worker
sudo systemctl start realm-sync-worker
Now, the sync worker is installed on a separate host. We need to edit the Realm Object Server’s configuration file to connect to the new sync worker. SSH back to the Realm Object Server’s instance, and open /etc/realm/configuration.yml
in your favorite text editor.
Find the Sync
section, uncomment the following lines, and add the internal DNS or IP address of your newly created server.
# /etc/realm/configuration.yml
sync:
servers:
- id: '0'
address: '<internal DNS or IP of sync worker server>'
port: 7800
The underlying IP address of the sync-worker can change as long as the ID of the sync worker remains the same, so in a failover scenario—bringing a new sync-worker online because an existing one has failed—you can just keep the ID value and spin up the new worker. In fact, if you use a DNS name, you could simply update your name server to point to the sync worker’s new address in the event of failure. For more information, read the Failover documentation.
Now that you’ve edited the configuration file, restart the Object Server. At the shell prompt:
systemctl restart realm-object-server
If you look at your system logs right now, you’ll see errors:
[syncProxy] internal error: Error: connect ECONNREFUSED
This is because we haven’t configured the sync worker to listen to the connection yet. We’ll do that in a moment, but there’s one other thing to do while on the Object Server’s machine: you’re going to need to copy its public key. This file is stored in /etc/realm/token-signature.pub
. This file needs to match on both the sync worker and Object Server. You can copy the file using scp:
scp /etc/realm/token-signature.pub sync-worker-ip:/etc/realm/token-signature.pub
(Replace sync-worker-ip
with the actual IP address or DNS name of the sync worker machine.) You can also use FTP to transfer the key, or even just copying the public key on the Object Server machine to your clipboard and pasting it into /etc/realm-token-signature.pub
on the sync worker.
Now, let’s SSH back to the sync worker, and edit its configuration file, /etc/realm/sync-worker-configuration.yml
, to uncomment the sync
section in its network
block:
# /etc/realm/sync-worker-configuration.yml
network:
sync:
listen_address: '0.0.0.0'
Note that 0.0.0.0
listens on all network interfaces; in practice, it’s more secure to specify the actual IP of the Realm Object Server.
Now, finally, restart the sync worker.
systemctl restart realm-sync-worker
You can follow this process to add more than one sync worker, spreading out the load for horizontal scaling.
Adding Backup Clients
For fault tolerance, you will want to regularly backup the realm data in the event of system failure or outage. Realm provides a continuous backup feature where each changeset synced to the sync worker is sent to the backup client.
To set up a backup client, the process is similar to setting up a sync worker:
# Set up the Enterprise Edition token
export PACKAGECLOUD_TOKEN=<the token you received via email>
# Set up the new package repository
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update
# Install the Sync Worker
sudo apt-get install realm-object-server-backup-client
Edit the backup client’s configuration file before starting it:
# /etc/realm/object-server-backup-client.yml
sync:
servers:
- id: '0'
address: '<internal DNS or IP of sync worker server>'
port: 7800
Now, start the client:
# Enable and start the service
sudo systemctl enable realm-object-server-backup-client
sudo systemctl start realm-object-server-backup-client
Finally, SSH to the sync worker and uncomment/modify its listen_address
line:
# /etc/realm/sync-worker-configuration.yml
network:
sync:
listen_address: '0.0.0.0'
Restart the sync worker.
systemctl restart realm-sync-worker
The backup client now has the full data set of all Realms set to the sync worker.
As a best practice, you should prepare to fail over to the backup client and start a sync worker on it so it can take over for a sync worker. To do this, you should pre-install the sync worker on the backup client without starting it. SSH to the backup client.
sudo apt-get install realm-sync-worker
Edit the configuration of the sync worker so it starts reading Realms from the same folder that the backup client is saving them to.
# /etc/realm/sync-worker-configuration.yml
# change the root_path to point to the backup client folder
storage:
root_path: '/var/lib/realm-object-server-backup-client'
# uncomment and correctly set the listen_address line
network:
sync:
listen_address: '0.0.0.0'
Now, in the event your original sync worker fails, you will be ready for failover by simply stopping the backup client process and starting the sync worker you have prepared.
sudo systemctl stop realm-object-server-backup-client
sudo systemctl start realm-sync-worker
For more details, read Failover in the Professional/Enterprise Edition documentation.
After failing over, you’ll need to start a new backup client that points to this server, perhaps triggered automatically through your tooling (e.g., an Auto Scaling Group resource that sends CloudFormation event notifications).
Congratulations — you’re done!
By now, we’ve shown you how to start and deploy the Realm Mobile Platform, integrate it into your apps to enable syncing and logins, add server-side Functions and event handling, and add additional sync workers and backup clients. We’ve covered the full depth of Enterprise Edition features, and you can return to this document whenever you need to a quick reference for your future projects. Good luck with your development work — and please feel free to contact your representative at Realm any time you need help or get stuck.
Receive news and updates from Realm straight to your inbox