DEV Community

Cover image for Realtime Offline-First Chat App in 100 Seconds
swyx
swyx

Posted on • Updated on

Realtime Offline-First Chat App in 100 Seconds

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
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (8)

Collapse
 
ltaljaard profile image
ltaljaard

One big issue with DataStore: github.com/aws-amplify/amplify-js/...

Collapse
 
undef_obj profile image
Ricardo

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.

Collapse
 
redbar0n profile image
Magne

it's been fixed now, as seen in the bottom of that thread.

Collapse
 
swyx profile image
swyx • Edited

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

Collapse
 
jwndev profile image
Jwn Dev

Hi swyx, is this tutorial still in effect? I wonder I followed it but got
`ompiled with problems:X

ERROR in ./src/App.js 12:0-17

export 'default' (imported as 'Amplify') was not found in 'aws-amplify' (possible exports: API, APIClass, AWSCloudWatchProvider, AWSKinesisFirehoseProvider, AWSKinesisProvider, AWSPinpointProvider, AmazonPersonalizeProvider, Amplify, Analytics, Auth, AuthModeStrategyType, Cache, ClientDevice, DataStore, Geo, Hub, I18n, Interactions, Logger, Notifications, Predicates, Predictions, PubSub, ServiceWorker, Signer, SortDirection, Storage, StorageClass, XR, graphqlOperation, syncExpression, withSSRContext)
`
Thank you, if you can cast me a littel light.

Collapse
 
syang profile image
syang

@swyx How could we send email notifications if the user is offline when chat? Is that possible?

Collapse
 
fatai2 profile image
fatai2

Thanks for this!

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

Collapse
 
swyx profile image
swyx

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