This content originally appeared on DEV Community and was authored by Salah Elhossiny
Amplify supports another type of API for interacting with AppSync: Amplify DataStore. DataStore has a different approach than a traditional GraphQL API.
Instead of interacting with the GraphQL API itself, using queries and mutations, DataStore introduces a client-side SDK that allows you to write to and read from a local store and persists this data locally using the local storage engine of the platform you are working with (i.e., IndexDB for web and SQLite for native iOS and Android).
DataStore then automatically syncs the local data to the GraphQL backend for you as updates are made both locally and remotely.
Using the DataStore SDK, you then only have to perform the
operations like save, update, and delete, writing directly to DataStore
itself. DataStore handles everything else for you: it syncs your data to
the cloud when you have an internet connection, and if you’re not
online, will queue it for the next time you’re connected.
About Amplify DataStore
Amplify DataStore is a combination of the following things:
AppSync GraphQL API.
Local storage repository and syncing engine that also persists data offline.
Client-side SDK for interacting with the local storage repository.
Special sync-enabled GraphQL resolvers (generated by the Amplify CLI) that enable sophisticated conflict detection and conflict resolution on the server.
Amplify DataStore Overview
When getting started with DataStore, you still create the API as we have done in past chapters. The main difference is, when creating the
API, you will enable conflict detection in the advanced settings of the CLI flow.
From there, to enable DataStore on the client, we need to create
models for DataStore to use to interact with the storage repository.
This can easily be done by just using the GraphQL schema you
already have and running a build command—amplify codegen
models—from the CLI.
Now, you are all set up and can begin interacting with DataStore.
Amplify DataStore Operations
To interact with the Store, first import the DataStore API from Amplify and the Model you’d like to use. From there, you can perform actions against the store.
Operations & Commands
Import the model and DataStore
API
import { DataStore } from '@aws-amplify/datastore';
import { Message} from './models';
Saving data
await DataStore.save(
new Message({
title: 'Hello World',
sender: 'Chris'
})
))
Reading data
const posts = await DataStore.query(Post);
Updating data
const message = await DataStore.query(Message, '123')
await DataStore.save(
Post.copyOf(message, updated => { updated.title = 'My new title'})
)
Observing/subscribing to changes in data for real-time functionality
const subscription = DataStore.observe(Message).subscribe(msg => {
console.log(message.model, message.opType, message.element)
});
DataStore Predicates
You can apply predicate filters against the DataStore using the fields
defined on your GraphQL type along with the following conditions
supported by DynamoDB:
Strings: eq | ne | le | lt | ge | gt | contains | notContains | beginsWith | between
Numbers: eq | ne | le | lt | ge | gt | between
Lists: contains | notContains
For example, if you wanted a list of all messages that have a title
that includes “Hello”:
const messages = await DataStore.query(Message, m => m.title('contains', 'Hello'))
You can also chain multiple predicates into a single operation:
const message = await DataStore .query(Message, m => m.title('contains', 'Hello').sender('eq', 'Chris'))
These predicates enable you to have many ways to retrieve different selection sets from your local data. Instead of retrieving the entire collection and filtering on the client, you are able to query from the store exactly the data that you need.
Building an Offline and Real-Time App with Amplify DataStore
Users of the app can create a new message and all other users will
receive the message in real time. If a user goes offline, they will
continue to be able to create messages. Once they are online, the
messages will be synced with the backend, and all other messages
created by other users will also be fetched and synced locally.
Our app will perform three types of operations against the DataStore
API:
save
Creating a new item in the DataStore; saves the item locally and
performs a GraphQL mutation behind the scenes.
query
Reading from the DataStore; returns a single item or list (array)
and performs a GraphQL query behind the scenes.
observe
Listening for changes (create, update, delete) in data and performs
a GraphQL subscription behind the scenes.
Creating the Base Project
To get started, we will create a new React project, initialize an
Amplify app, and install the dependencies.
The first thing we will do is create the React project:
~ npx create-react-app rtmessageboard
~ cd rtmessageboard
Next, we will install the local dependencies.
Amplify supports a full installation of Amplify, and scoped (modular)
installations for specific APIs. Scoped packages reduce the bundle
size, since we’re installing only the code that we are using. Since we
are only using the DataStore API, we can install the scoped DataStore
package.
We will also install Ant Design (antd) for styling, React Color
(react-color) for an easy-to-use color picker, and the scoped
dependency for Amplify Core in order to still configure the Amplify
app with aws-exports.js:
~ npm install @aws-amplify/core @aws-amplify/datastore antd react-color
Next, initialize a new Amplify project:
~ amplify init
# Follow the steps to give the project a name, environment name, and set the default text editor.
# Accept defaults for everything else and choose your AWS Profile
Creating the API
Now we will create the AppSync GraphQL API:
~ amplify add api
? Please select from one of the below mentioned services:GraphQL
? Provide API name: rtmessageboard
? Choose the default authorization type for the API: API key
? Enter a description for the API key: public
? After how many days from now the API key should expire (1-
365): 365 (or your
preferred expiration)
? Do you want to configure advanced settings for the GraphQL
API: Yes
? Configure additional auth types: N
? Configure conflict detection: Y
? Select the default resolution strategy: Auto Merge
? Do you have an annotated GraphQL schema: N
? Do you want a guided schema creation: Y
? What best describes your project: Single object with
fields
? Do you want to edit the schema now: Y
Update the schema with the following type:
type Message @model {
id: ID!
title: String!
color: String
image: String
createdAt: String
}
Now that we have created the GraphQL API, and we have a GraphQL schema to work with, we can create the models we’ll need for working the local DataStore API (based on the GraphQL schema):
~ amplify codegen models
This will create a new folder in our project called models. Using the models in this folder, we can start interacting with the DataStore API. Deploy the API:
~ amplify push --y
Writing the Client-Side Code
First, open src/index.js and configure the Amplify app by adding the following code below the last import:
import 'antd/dist/antd.css'
import Amplify from '@aws-amplify/core'
import config from './aws-exports'
Amplify.configure(config)
Next, open App.js and update it with the following code:
/* src/App.js */
import React, { useState, useEffect } from 'react'
import { SketchPicker } from 'react-color'
import { Input, Button } from 'antd'
import { DataStore } from '@aws-amplify/datastore'
import { Message} from './models'
const initialState = { color: '#000000', title: '' }
function App() {
const [formState, updateFormState] = useState(initialState)
const [messages, updateMessages] = useState([])
const [showPicker, updateShowPicker] = useState(false)
useEffect(() => {
fetchMessages()
const subscription = DataStore.observe(Message).subscribe(() => fetchMessages())
return () => subscription.unsubscribe()
}, [])
async function fetchMessages() {
const messages = await DataStore.query(Message)
updateMessages(messages)
}
function onChange(e) {
if (e.hex) {
updateFormState({ ...formState, color: e.hex})
}
else {
updateFormState({ ...formState, [e.target.name]: e.target.value})
}
}
async function createMessage() {
if (!formState.title) return
await DataStore.save(new Message({ ...formState }))
updateFormState(initialState)
}
return (
<div style={container}>
<h1 style={heading}>Real Time Message Board</h1>
<Input
onChange={onChange}
name="title"
placeholder="Message title"
value={formState.title}
style={input}
/>
<div>
<Button
onClick={() => updateShowPicker(!showPicker)}
style={button}
>Toggle Color Picker</Button>
<p>Color:
<span
style={{fontWeight: 'bold', color:
formState.color}}>{formState.color}
</span>
</p>
</div>
{
showPicker && (
<SketchPicker
color={formState.color}
onChange={onChange} /
>
)
}
<Button type="primary" onClick={createMessage}>Create Message</Button>
{
messages.map(message => (
<div
key={message.id}
style={{...messageStyle, backgroundColor:message.color}}
>
<div style={messageBg}>
<p style={messageTitle}>{message.title}</p>
</div>
</div>
))
}
</div>
);
}
const container = { width: '100%', padding: 40, maxWidth: 900 }
const input = { marginBottom: 10 }
const button = { marginBottom: 10 }
const heading = { fontWeight: 'normal', fontSize: 40 }
const messageBg = { backgroundColor: 'white' }
const messageStyle = { padding: '20px', marginTop: 7, borderRadius: 4 }
const messageTitle = { margin: 0, padding: 9, fontSize: 20}
export default App;
Conclusion
Here are a few things to keep in mind from this chapter:
Amplify enables two different APIs to interact with AppSync: the API category as well as DataStore.
When using DataStore, you are no longer sending HTTP requests directly to the API. Instead, you are writing to the local storage engine, and DataStore then takes care of syncing to and from the cloud.
Amplify DataStore works offline by default.
References:
Notes from Book: Full Stack Serverless: Modern Application Development with React, AWS, and GraphQL
By: Nader Dabit
Part 1 : https://dev.to/salah856/modern-full-stack-serverless-part-i-34cb
Part 2 : https://dev.to/aws-builders/modern-full-stack-serverless-part-ii-94i
Part 3 : https://dev.to/aws-builders/modern-full-stack-serverless-part-iii-ha
Part 4 : https://dev.to/aws-builders/modern-full-stack-serverless-part-iv-54bl
Part 5: https://dev.to/aws-builders/modern-full-stack-serverless-part-v-35j6
Part 6: https://dev.to/aws-builders/modern-full-stack-serverless-part-vi-25e3
This content originally appeared on DEV Community and was authored by Salah Elhossiny
Salah Elhossiny | Sciencx (2021-06-04T07:13:27+00:00) Modern Full-Stack Serverless, Part VII. Retrieved from https://www.scien.cx/2021/06/04/modern-full-stack-serverless-part-vii/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.