loading...
Cover image for Realtime Offline-First Chat App in 100 Seconds with Amplify DataStore, React, and GraphQL

Realtime Offline-First Chat App in 100 Seconds with Amplify DataStore, React, and GraphQL

swyx profile image shawn swyx wang πŸ‡ΈπŸ‡¬ ・4 min read

Amplify DataStore provides a persistent on-device storage repository for you to write, read, and observe changes to data if you are online or offline, and seamlessly sync to the cloud as well as across devices.

It is free, open source, and supported by the AWS Amplify team, and I wanted to show you how easy it is to use it to add realtime, offline first CRUD features to your app! We'll use React in this example, but you can easily use this guide for adding realtime, offline-first CRUD to an app built with any framework.

100 Second Video Version

Youtube: https://youtu.be/pSSfTWqSXbU

Dev.to Embed:

Text Based Version - 3 Steps

The stuff below is the script for the video above, so you can copy/paste!

Step 1: Setup the React Chat App

Assuming you have setup the Amplify CLI, we're going to spin up a standard React app and install a special demo Chat component I've prepared under the react-demos package:

npx create react-app amplifychatapp
cd amplifychatapp
yarn add react-demos # or npm install react-demos

Let's try out this demo component to get familiar with it!

// src/App.js
import React from 'react'
import { Chat, useChatLocalState } from 'react-demos'

export default function App() {
  const {
    currentUser,
    sendMessage,
    loginUser,
    messages,
    usersOnline,
  } = useChatLocalState()
  return (
    <div>
      <Chat
        {...{
          currentUser,
          sendMessage,
          loginUser,
          messages,
          usersOnline,
        }}
      />
    </div>
  )
}

Now we can start up our app with npm run start and it works! This data isn't stored or shared though - when you reload that page or load it in an incognito browser, the messages start over from scratch. Not much of a chat app!

Step 2: Setup the Amplify DataStore

We'll init a new Amplify project and amplify add api, making sure to enable "Conflict Resolution" (which enables the Amplify DataStore):

yarn add aws-amplify @aws-amplify/datastore 
# or use npm install

amplify init 
# go with all default answers... and when you are done...

amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: # Any Name Is Fine
? Choose the default authorization type for the API API key
? Enter a description for the API key: # Any Description Is Fine
? After how many days from now the API key should expire (1-365): 7
? Do you want to configure advanced settings for the GraphQL API Yes, I want to make some additional changes.
? Configure additional auth types? No
? Configure conflict detection? Yes # IMPORTANT
? Select the default resolution strategy Auto Merge
? Do you have an annotated GraphQL schema? No
? Choose a schema template: Single object with fields (e.g., β€œTodo” with ID, name, description)
# some instructions here...
? Do you want to edit the schema now? Yes

This will open up your editor where we can specify the GraphQL schema for the DataStore (it is exactly the same schema definition language as GraphQL Transform for AWS AppSync). We'll paste in this very simple schema:

# /amplify/backend/api/YOURAPINAME/schema.graphql
type User @model {
  id: ID!
  name: String
}

type Message @model {
  id: ID!
  user: String
  text: String
}

Save the file and amplify push --y to kick off provisioning the AWS backend!

While that's going, we will run amplify codegen models to generate the DataStore models we will use in our React app.

Step 3: Wire up DataStore with React

Now let's put it into use:


import React from "react";
import { DataStore } from "@aws-amplify/datastore";
import { User, Message } from "./models";
import { Chat } from "react-demos";
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
Amplify.configure(awsconfig); // will not sync if you forget this

function App() {
  const [currentUser, setCurrentUser] = React.useState(null);
  const [usersOnline, setUsersOnline] = React.useState([]);
  const [messages, setMessages] = React.useState([]);

  React.useEffect(() => {
    fetchMessage();
    DataStore.observe(Message).subscribe(fetchMessage);
  }, []);
  React.useEffect(() => {
    fetchMessage();
    DataStore.observe(User).subscribe(() => 
      DataStore.query(User).then(setUsersOnline)
    );
  }, []);
  async function fetchMessage() {
    const _Messages = await DataStore.query(Message);
    setMessages(_Messages);
  }

  async function loginUser(name) {
    const user = await DataStore.save(new User({ name }));
    setCurrentUser(user);
  }
  async function sendMessage(text) {
    await DataStore.save(
      new Message({
        user: currentUser.name,
        text,
      })
    );
  }

  return (
    <div>
      <Chat
        {...{
          currentUser,
          sendMessage,
          loginUser,
          messages,
          usersOnline,
        }}
      />
    </div>
  );
}

export default App;

And there you have it - a realtime, offline persisting chat app with Amplify DataStore!

Conclusion

Now you've had a taste, be sure to head to the docs to get a fuller understanding, or watch Richard Threlkeld's Re:Invent 2019 talk where Amplify DataStore was first introduced!

P.S. if you are worried about incurring AWS charges following this guide, just run amplify delete at the end to delete everything you just set up in one command! The Amplify DataStore itself carries no charge to use, but it uses AWS AppSync for storage so you should check that pricing as you plan for production usage.

Share

Liked this format/tutorial/video? Got requests for more? Please comment and share it with a Tweet or subscribe to my YouTube!

Posted on by:

swyx profile

shawn swyx wang πŸ‡ΈπŸ‡¬

@swyx

Infinite Builder πŸ‘·πŸ½β€β™‚οΈI help people LearnInPublic β€’ more at http://twitter.com/swyx

Discussion

markdown guide
 
 

We're actually finishing up some service changes that will allow you to do this, along with some configuration options in the client. The feedback in that thread was very helpful as requirements for the work. Keep an eye out on that thread in the coming month or so for an update.

 

this is a very important issue indeed. unfortunately I have no updates to offer on this one at the moment.

 

Thanks for this!

Still having difficulty getting Datastore to work with 1:N relationships

 

have you filed an issue somewhere i can look up? best way to give AWS feedback is via github issues