DEV Community

Cover image for Realm Database, Expo SDK 49 and Expo Router Getting Started
Aaron K Saunders
Aaron K Saunders

Posted on

Realm Database, Expo SDK 49 and Expo Router Getting Started

Steps for building a simple mobile application using Expo SDK, Expo Router, and MongoDB Realm in React Native and plain Javascript.

Realm is a fast, scalable alternative to SQLite with mobile to cloud data sync that makes building real-time, reactive mobile apps easy.

I did this tutorial for those who want to build in javascript and not typescript but also to show my approach to using Realm with Expo Router's file-based approach to routing.

This is a companion blog post to go along with this video.

Getting Started

Create the application

npx create-expo-app@latest --template blank@sdk-49 app-with-realm
Enter fullscreen mode Exit fullscreen mode

change into project directory

cd app-with-realm
Enter fullscreen mode Exit fullscreen mode

Install additional libraries and packages for expo-router

npx expo install expo-router@latest react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar react-native-gesture-handler
Enter fullscreen mode Exit fullscreen mode

Make modifications to support expo-router in package.json

{
  "main": "expo-router/entry"
}
Enter fullscreen mode Exit fullscreen mode

Not using web so skipping that part of documentation, but add the scheme app.json

"scheme": "app-with-realm",
Enter fullscreen mode Exit fullscreen mode

update babel.config.js to include the new plugin.

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['expo-router/babel'],
  };
};
Enter fullscreen mode Exit fullscreen mode

Lets add the index page and then test it is working, remember the files all live in a new app directory that you have to create. /app/index.js

// /app/index.js

import { Text } from 'react-native';

export default function Page() {
  return <Text>Home page</Text>;
}
Enter fullscreen mode Exit fullscreen mode

Installing Realm

npm install realm @realm/react
Enter fullscreen mode Exit fullscreen mode

to build locally run do the following, if not you will continue to get a pod install error

Error: Missing Realm constructor. Did you run "pod install"? Please see https://realm.io/docs/react-native/latest/#missing-realm-constructor for troubleshooting*

npx expo prebuild
Enter fullscreen mode Exit fullscreen mode

then to run on ios, you will see the pod file get installed appropriately

npm run ios
Enter fullscreen mode Exit fullscreen mode

Now we need to create our schema will be using the one from the realm example but in plain javascript. Add file to app directory

// app/Task.js

import Realm, { BSON } from "realm";

export class Task extends Realm.Object {
  _id
  description 
  isComplete 
  createdAt 

  static primaryKey = "_id";
  static schema = {
    name: "Task",
    primaryKey: "_id",
    properties: {
      _id: 'uuid',
      description: "string",
      createdAt: {
        type: "date",
        default: new Date(),
      },
      isComplete: {
        type: "bool",
        default: false,
        indexed: true,
      },
    },
  };

  constructor(realm, description) {
    console.log("in constructor");
    super(realm, {
      _id: new BSON.UUID(),
      description,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Now lets wrap our whole app with the provider by creating a layout at the app route. Add this file _layout.js to the root of you app directory.

We use the schema we created Task as a parameter to the RealmProvider

// app/_layout.js

import 'react-native-get-random-values'
import { Stack} from "expo-router";
import { RealmProvider } from "@realm/react";
import { Task } from './Task';

export default function AppLayout() {

  return (
    <RealmProvider schema={[Task]}>
      <Stack />
    </RealmProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

update index.js so that we can query our database using a useQuery hook provided by realm.

// /app/index.js

import { Text, View } from "react-native";
import { useQuery } from "@realm/react";
import { Task } from "./Task";

export default function Page() {
  const tasks = useQuery(Task);
  console.log(tasks);
  return (
    <View>
      <Text>TASK LIST</Text>
      <Text>{JSON.stringify(tasks, null, 2)}</Text>
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

lets add some UI to add a Task

// /app/index.js

import {
  Text,
  TextInput,
  View,
  StyleSheet,
  TouchableOpacity,
} from "react-native";
import { useQuery } from "@realm/react";
import { Task } from "./Task";
import { useRef } from "react";

export default function Page() {
  // ref to hold description
  const descriptionRef = useRef("");

  // get the tasks
  const tasks = useQuery(Task);

  return (
    <View style={{ height: Dimensions.get("screen").height - 132 }}>
      <Text style={styles.title}>TASK LIST</Text>
      {/* input for description */}
      <TextInput
        placeholder="Enter New Task"
        autoCapitalize="none"
        nativeID="description"
        multiline={true}
        numberOfLines={8}
        value={descriptionRef.current}
        onChangeText={(text) => {
          descriptionRef.current = text;
        }}
        style={styles.textInput}
      />
      {/*  button to save the new task */}
      <TouchableOpacity
        style={styles.button}
        onPress={() => {
          createNewTask();
        }}
      >
        <Text style={styles.buttonText}>SAVE TASK</Text>
      </TouchableOpacity>
      <Text>{JSON.stringify(tasks, null, 2)}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    margin: 16,
  },
  title: {
    fontSize: 18,
    margin: 16,
    fontWeight: "700",
  },
  label: {
    marginBottom: 8,
    fontSize: 18,
    fontWeight: "500",
    // color: "#455fff",
  },
  textInput: {
    fontSize: 20,
    borderWidth: 1,
    borderRadius: 4,
    // borderColor: "#455fff",
    paddingHorizontal: 8,
    paddingVertical: 4,
    marginBottom: 0,
    marginHorizontal: 16,
  },
  button: {
    backgroundColor: "grey",
    padding: 10,
    borderRadius: 5,
    marginTop: 8,
    marginLeft: 16,
    width: 120,
  },
  buttonText: {
    color: "white",
    textAlign: "center",
    fontWeight: "600",
    fontSize: 12,
  },
});
Enter fullscreen mode Exit fullscreen mode

Now add the function, createNewTask

to save the task to the realm database. We will us the useRealm hook in this function

import { useRealm } from "@realm/react";
Enter fullscreen mode Exit fullscreen mode

then inside the component

const realm = useRealm();
Enter fullscreen mode Exit fullscreen mode

Then in the component add the code for the createNewTask function

const createNewTask = () => {
  realm.write(() => {
     const newTask = new Task(realm, descriptionRef.current);

     // clear input field
         descriptionRef.current = "";

     // return task
     return newTask;
  });
};
Enter fullscreen mode Exit fullscreen mode

Run the code and add a task

Image description

Lets add a component to render the tasks in a FlatList

import { useRealm } from "@realm/react";
import { StyleSheet, View, Text, Dimensions, Pressable } from "react-native";
import { FlatList } from "react-native-gesture-handler";

export const TaskList = ({ data }) => {
  const realm = useRealm();

  const renderItem = ({ item }) => (
    <View style={styles.row}>
      <View style={styles.item}>
        <View style={{ display: "flex", flex: 12 }}>

          <Text style={{ fontSize: 22, fontWeight: 'bold', marginBottom:8 }}>{item.description}</Text>
          <Text style={{ fontSize: 18, marginBottom:4 }}>{item.createdAt.toString()}</Text>
          <Text style={{ }}>{item._id + ""}</Text>
        </View>
        <View style={{ display: "flex", alignSelf: "center" }}>
          <Pressable
            onPress={() => onToggleStatus(item)}
            style={[styles.status, item.isComplete && styles.completed]}
          >
            <Text style={[styles.icon]}>{item.isComplete ? "" : ""}</Text>
          </Pressable>
        </View>
      </View>
      <Pressable onPress={()=>onDelete(item)} style={styles.deleteButton}>
        <Text style={styles.deleteText}>Delete</Text>
      </Pressable>
    </View>
  );

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={(item) => item._id + ""}
    />
  );
};

const styles = StyleSheet.create({
  item: {
    display: "flex",
    flexDirection: "row",
  },
  row: {
    padding: 20,
    borderBottomWidth: 1,
    borderBottomColor: "#ccc",
    width: Dimensions.get("screen").width,
  },
  icon: {
    textAlign: "center",
    fontSize: 20,
    fontWeight: "bold",
    textAlignVertical: "center",
  },
  status: {
    width: 32,
    height: 32,
  },
  deleteButton: {
    backgroundColor: "red",
    margin: 8,
    marginLeft: 0,
    paddingHorizontal: 16,
    paddingVertical: 8,
    borderRadius: 8,
    width: 100,
  },
  deleteText: {
    textAlign: "center",
  },
});
Enter fullscreen mode Exit fullscreen mode

Add Toggle function

const onToggleStatus = (task) => {
  console.log(task);
  realm.write(() => {
    task.isComplete = !task.isComplete;
  });
};
Enter fullscreen mode Exit fullscreen mode

Add delete Function

const onDelete = (task) => {
  console.log(task);
  realm.write(() => {
    realm.delete(task);
  });
};

Enter fullscreen mode Exit fullscreen mode

Image description

Links

Social Media

Top comments (7)

Collapse
 
jakecarpenter profile image
Jake Carpenter • Edited

Thanks for the post! It looks very interesting and I’m going to save it for the weekend to really dig in. Seems like a great way to get my feet wet with Expo Router.

Collapse
 
aaronksaunders profile image
Aaron K Saunders

Hopefully you find it helpful, I also have a full series here on expo router - youtube.com/playlist?list=PL2PY2-9...

Collapse
 
devlugo profile image
Rodolfo Lugo

Great video! I've been really enjoying your Realm + React Native content.

I'm currently facing an issue, and I've been stuck on it for about four weeks now. Everything was smooth until I installed the Realm/React library and created the app layout. Suddenly, I encountered the error message: "Could not find the Realm Binary. Please consult our troubleshooting guide..." (Note that everything was working fine before creating the app layout.)

I've already tried the troubleshooting guide, but unfortunately, I'm still facing the same issue.

Just to provide some context, I'm working on a Windows machine with an Android device. Any suggestions or ideas on how to resolve this would be greatly appreciated!

Collapse
 
ayekpleclemence profile image
Ayekple Clemence

According to the Realm documentation, you have to insall expo-dev-client to resolve this issue
npm install expo-dev-client

mongodb.com/docs/realm-sdks/js/lat...

Collapse
 
stanroy profile image
Stan Roy

Were you able to solve the problem? I am having the exact same problem. Windows machine with iOS device. Please advise.

Collapse
 
mahmoud_bitar profile image
Mahmoud bitar

Your article is genuinely incredible. Thanks for pointing out how to do the setup with JavaScript. I struggled with the realm docs and almost give up on some errors.

I also liked your way of defining the the schema objects and the creation for them.

Thanks a lot!

Collapse
 
praval791 profile image
Praval

I am working on Windows and using expo go for android, I followed all of your instructions for setup and updated the expo from 48 to version 49, fixed the dependencies, and also prebuild the app still getting the pods error.
any suggestion to solve this.