Build fast React Native apps with AWS Amplify
Published: 2022-08-19 18:20:11



## Overview

In this article, we will be building a mobile application with React Native using Expo SQLite adapter as a storage adapter for DataStore.

By the end of the of article, you will be able to build a contact app using the SQLite adapter for storage as shown in the video below:

{% embed https://youtu.be/i4pdClzRdDY %}

Expo enables developers to create hybrid JavaScript applications from the comfort of their terminals. Expo has an adapter that gives your app access to a database that can be queried through a [WebSQL](https://www.w3.org/TR/webdatabase/)-like API called `expo-sqlite`. The database is persisted across restarts of your app.

Datastore allows you to interact with your data offline first, and it has a sync engine behind the scenes to send data to the backend. DataStore provides a model for using distributed or shared data without writing any additional code for implementing online and offline states. This programming model ensures that working with distributed or shared data is as easy as working with local data. DataStore provides data modeling using GraphQL and converts it to models that you can use in building JavaScript, Android, iOS, and Flutter applications. 

With DataStore, at runtime models are passed into a *Storage Engine* that has a ***Storage Adapter***. Amplify ships with default Storage Adapter implementations, such as SQLite and IndexedDB.

## Pre-requisites
- Nodejs ≥v14 installed
- Knowledge of JavaScript and React
- AWS Amplify CLI installed

```bash
npm install -g @aws-amplify/cli
```


- AWS Amplify configured

```bash
amplify configure
```

- Expo CLI installed

```bash
npm install -g expo-cli
```

- Expo Go (installed from your mobile playstore)

## Building the React Native app with Expo SQLite and Datastore

Let’s get started by initializing a new Expo app. Run this command on your terminal

```bash
expo init ReactAmplifyDataStoreExpo
```
This will create a new React Native application. Change directory and initialize amplify on the directory with these commands

```bash
cd AmplifyDataStoreExpo
npx amplify-app@latest
```

## Installing Dependencies

Next, install the following dependencies with expo

```bash
expo install aws-amplify @aws-amplify/datastore-storage-adapter expo-sqlite expo-file-system @react-native-community/netinfo @react-native-async-storage/async-storage
```

- `aws-amplify`: this package will enable us to build our AWS cloud-enabled mobile application.
- `@aws-amplify/datastore-storage-adapter`: this is the SQLite storage adapter for Amplify Datastore
- `expo-sqlite`: this package gives the app access to a database that can be queried through a [WebSQL](https://www.w3.org/TR/webdatabase/)-like API.
- `expo-file-system`: This package provides access to the local file system on the device.
- `@react-native-community/netinfo`: this package is the React Native Network Info API for Android, iOS, macOS, Windows & Web. It allows you to get information on Connection type and Connection quality
- `@react-native-async-storage/async-storage`: This is an asynchronous, unencrypted, persistent, key-value storage system for React Native.

Next, we go on to create the model for our application.

## Generating model with Amplify

The first step to building an app backed by datastore is by defining a **schema.** The schema contains data types and relationships that represent the app's functionality. In our app, we will create a schema for a simple contact application.
Head over to `amplify/backend/api/schema.graphql`, delete the content, and add these lines of code.

```graphql
type Contact @model {
  id: ID!
  name: String!
  phone: String!
  email: String
}
```
Here, our Contact model has `id`, `name,` `phone`, and `email` as properties. Let’s now go ahead to create the graphql generated schema and models. Run this command on your terminal

```bash
npm run amplify-modelgen
```
This will create an `src/models` folder with the model and graphql schema. We are making progress!

Next, we initialize the amplify backend. Run this command on your terminal:

```bash
amplify init
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
Using default provider  awscloudformation
? Select the authentication method you want to use: AWS profile
```
This will also create an `aws-exports.js` in the `src` directory. Awesome.
Next, we go ahead to deploy the amplify backend to the cloud. Run this command to do that:

```bash
amplify push
```
For this option, `? Do you want to generate code for your newly created GraphQL API No`, choose `No`. We are choosing no here because we’ll be using the datastore API. This will take some time to deploy, depending on your network speed.


## Importing Dependencies

Navigate to `App.js`, delete the content and add these lines of code:

```javaScript
import { DataStore } from 'aws-amplify';
import { ExpoSQLiteAdapter } from '@aws-amplify/datastore-storage-adapter/ExpoSQLiteAdapter';
import Amplify from '@aws-amplify/core';
import config from './src/aws-exports'
import React, { useState, useEffect } from 'react'
import { Text, View, TextInput, Button } from 'react-native'
import { Contact } from './src/models'
```
Here, we imported DataStore from amplify, we also got the `ExpoSQLiteAdapter` from amplify datastore storage adapter package and the `Contact` model from models.


## Configuring Amplify and DataStore with ExpoSQLiteAdapter

Next, add these lines of code to `App.js`:

```javascript
Amplify.configure(config)

DataStore.configure({
  storageAdapter: ExpoSQLiteAdapter
});
```
Here, we configure Amplify with `config` from `aws-exports`, and then we set `ExpoSQLiteAdapter` as the `storageAdapter` for Datastore. Let’s move on.

## Implementing UI Inputs

```javascript
  //App.js
  const initialState = { name: '', email: '', phone: '' }
  
  function App() {
    return(
      <View style={container}>
        <Text style={heading}> My Contacts </Text>
        <TextInput
          onChangeText={v => onChangeText('name', v)}
          placeholder='Contact name'
          value={formState.name}
          style={input}
        />
        <TextInput
          onChangeText={v => onChangeText('email', v)}
          placeholder='Contact email'
          value={formState.email}
          style={input}
        />
        <TextInput
          onChangeText={v => onChangeText('phone', v)}
          placeholder='Contact phone'
          value={formState.phone}
          style={input}
        />
        <Button onPress={createContact} title='Create Contact' />
    </View>
  )
}
```
Here, we start by setting the initial state of the input fields. We also have `TextInput` fields and a `Button` that invokes the `createContact` function `onPress`. Let’s go ahead and create the `createContact` function. 

## Implementing Create Contact Functionality

Add these lines of code to `App.js`

```javascript
    
const [formState, updateFormState] = useState(initialState)

async function createContact() {
    if (!formState.name && !formState.email && !formState.phone) return
    await DataStore.save(new Contact({ ...formState }))
    updateFormState(initialState)
  }
```
Then, we have two state variables, the first `formState` will get the values of the input fields and `updateFormState` will update the state when the `onChangeText` event is triggered.
Next, we have a `createContact` function. First, we have a condition that validates if the `name`, `email`, and `phone` fields are empty, if they are, the function returns false, else, we go on to save the field values to Datastore.


## Implementing View Contact Functionality

Add these lines of code to `App.js`
```javascript
  const [contacts, updateContacts] = useState([])

  async function fetchContacts() {
    const contacts = await DataStore.query(Contact)
    updateContacts(contacts)
  }
```
The other variable is an array that holds the contacts we will be fetching from DataStore. Next, we have a `fetchContacts` function that queries the `Contact` model and then we update the `contacts` array.
```javascript
  {
    contacts.map(contact => (
      <View key={contact.id}>
        <View style={contactBg}>
          <Text style={contactText}>Name: {contact.name}</Text>
          <Text style={contactText}>Email: {contact.email}</Text>
          <Text style={contactText}>Phone: {contact.phone}</Text>
        </View>
      </View>
    ))
  }
```
Here, we mapped through the contacts array to render the contact `name`, `email`, and `phone`.


## Implementing onChangeText functionality

Add these lines of code to `App.js`
```javascript
function onChangeText(key, value) {
    updateFormState({ ...formState, [key]: value })
}
```
When the onChangeText event is triggered the `formState` is updated with the field values.


## Implementing Subscriptions with DataStore

Add these lines of code to `App.js`
```javascript
useEffect(() => {
  fetchContacts()
  const subscription = DataStore.observe(Contact).subscribe(() => fetchContacts())
  return () => subscription.unsubscribe()
})
```

Finally, we have a `useEffect()` where we call the `fetchContacts()`, and then we do something interesting. We create a graphql subscription with Datastore. So if you’re updating the contacts on the web, or iOS app, or Android, you can see those updates in real-time. Awesome.


## Adding some Styles

We should go ahead to add the styles. Add the lines of code to the bottom of the codebase in `App.js`
```javascript
const container = { padding: 20, paddingTop: 80 }
const input = { marginBottom: 10, padding: 7, backgroundColor: '#ddd' }
const heading = { fontWeight: 'normal', fontSize: 40 }
const contactBg = { backgroundColor: 'white' }
const contactText = { margin: 0, padding: 9, fontSize: 20 }

export default App
```
Awesome!

Now let’s go ahead and run the app. Run this command on your terminal.
```bash
expo start
```
You should have something like this in the terminal
```bash

➜  ReactAmplifyDataStoreExpo git:(master) ✗ expo start

This command is being executed with the global Expo CLI.
To use the local CLI instead (recommended in SDK 46 and higher), run:
› npx expo start

Starting project at /Users/mac/Desktop/sammy/ReactAmplifyDataStoreExpo
Starting Metro Bundler
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
█ ▄▄▄▄▄ █▀▄█▀     █ ▄▄▄▄▄ █
█ █   █ █▄   ▄██▀▀█ █   █ █
█ █▄▄▄█ █ ▀█▀█▄▀ ██ █▄▄▄█ █
█▄▄▄▄▄▄▄█ ▀▄█ █▄▀ █▄▄▄▄▄▄▄█
█▄▄▀ █▀▄▄██ ▀▄ ▀██▀  ▄▀▄▄▀█
█ ▄▄▀▄▀▄▀▄▄▄█▄  ▀▄▄▀ ▀▀█▄▄█
█▄▄ ▄ █▄▄██▀██▄ █▀█ ▄█ ██▀█
█▄▀██▀█▄ █▄█ ▄▀██ ▄▄ ▀▀██▄█
█▄▄██▄▄▄▄  ▀▀██▄  ▄▄▄ █ ▄ █
█ ▄▄▄▄▄ █▄▄▀▀▄▄▄  █▄█  ▀▄ █
█ █   █ █▀▀ ▄█ ▀▀▄ ▄▄ █▀▄██
█ █▄▄▄█ █▀▄ ██ ▄█  █▄  ▄█▄█
█▄▄▄▄▄▄▄█▄██▄█▄▄█▄███▄▄█▄▄█

› Metro waiting on exp://192.168.104.200:19000
› Scan the QR code above with Expo Go (Android) or the Camera app (iOS)

› Press a │ open Android
› Press i │ open iOS simulator
› Press w │ open web

› Press r │ reload app
› Press m │ toggle menu

› Press ? │ show all commands

Logs for your project will appear below. Press Ctrl+C to exit.
Started Metro Bundler
```
Now launch the Expo Go app on your mobile, and scan the QR code. You should have something like this:

![](https://paper-attachments.dropbox.com/s_CA3C48D695A46A6FCB7A7FA0B21D8B719413205D176C9F34D85CBC4BBF98B37A_1660747043337_exposqlite.jpeg)


Awesome, you can go ahead to install
```bash
npm i react-native-web react-dom
```
This will enable viewing the app on your web browser.

## Conclusion

Amplify DataStore allows you to start persisting data locally to your device with DataStore, even without an AWS account. In this article, we learned about Expo SQLite and we went on to build a React native app with Expo SQLite adapter and AWS Amplify DataStore.