Front-End Web & Mobile
NEW: Lazy loading & nested query predicates for AWS Amplify DataStore
Today, we’re announcing three major enhancements to Amplify DataStore to make working with relational data easier: lazy loading, nested query predicates, and type enhancements. DataStore provides frontend app developers the ability to build real-time apps with offline capabilities by storing data on-device (web browser or mobile device), and automatically synchronizing data to the cloud and across devices on an internet connection.
The three new relational data enhancements are:
1. Lazy load related data: You can now asynchronously load related data for hasOne, hasMany, belongsTo, or manyToMany.
- If a post has many comments, you can lazy load comments using the async
toArray()
function:await post.comments.toArray()
. - You can also lazy load hasOne relationships, for example:
await post.author
. - DataStore also takes advantage of JavaScript’s built-in async iterator support to make it easier for you to author
for
loops:
for await (const comment of post.comments) {
console.log(comment) // iterates over each comment!
}
2. Nested query predicates: You can now query based on conditions of related models.
- For example, if you only want to get the comments of posts with a title starting with “Amplify”, you can now do the following:
await DataStore.query(Comment, c => c.post.title.beginsWith(“Amplify”)
3. Improved types for predicates: Previously predicate operators were passed as strings. The new predicate types allow you to specify the predicate operators as a function call.
- Before:
DataStore.query(Post, p => p.id(‘eq’, ‘123’))
- Now:
DataStore.query(Post, p => p.id.eq(‘123’))
Step 1: Deploy Amplify app with GraphQL API
Let’s walk through all of these enhancements by checking out a blog post demo app:
- Users can create blog posts, comments, and replies
- Users can load comments by clicking on the “show comments” button
- Users can export the blog posts, comments, and replies as a text file
- Users can filter blog posts based on their comment replies’ content
To get started, we’ll deploy a React app based on an existing sample repository to Amplify Hosting. This allows us to quickly get started with an app backend that contains a GraphQL API backed by DynamoDB. This repository also contains some React components to let users create blog posts, comments, and replies.
Note: To deploy this sample app, you need to have an AWS Account.
As part of this process, Amplify Hosting forks the sample repository into your GitHub account. From there it schedules a new fullstack build and hosts your web app once the build is complete.
While the build is running, you can move on to Step 2 to get your local development environment configured.
Step 2: Clone React app code
Let’s clone the newly forked repository into your local development environment. Go to GitHub to find the newly forked repository under your profile. Follow the instructions on GitHub to clone the repository locally. The terminal command should look something like this:
git clone git@github.com:<YOUR_GITHUB_USERNAME>/amplify-lazy-blog.git
Once the repository is cloned, let’s go into the React project, install the node dependencies, and start the local development server:
cd amplify-lazy-blog
npm install
npm start
Play with the app on http://localhost:3000
to add a few sample blog posts, comments, and replies. This will come in handy later as we flesh out the remaining parts of the app.
Step 3: Build export feature with lazy loading
To show case the new lazy loading functionality, we’ll build an “export” feature that formats all the blog posts, their comments, and replies into a text file. To achieve this, we need to iterate over all posts, their comments, and each comment’s replies.
Go to the App.js file leverage the new for await (...)
async iterator syntax to iterate through all the records.
async function exportAsFile() {
let output = ""
for await (const post of posts) {
output += `${post.title}\n\n`
output += `${post.content}\n\n`
output += `Comments:\n`
for await (const comment of post.comments) {
output += `- ${comment.content} @ ${comment.createdAt}\n`
for await (const reply of comment.replies) {
output += ` - ${reply.content} @ ${reply.createdAt}\n`
}
}
output += `-------\n`
}
downloadString(output)
}
Your app can now allow users to download all the blog contents in a pre-formatted file.
Data models that are connected by a “hasMany” relationship provide a toArray()
function. This allows you to rewrite the for await (...)
loop to the following:
// Using for await (...) loop
for await (const reply of comment.replies) {
output += ` - ${reply.content} @ ${reply.createdAt}\n`
}
// Using `.toArray()` function
const replies = await comment.replies.toArray()
output += replies.map(reply => ` - ${reply.content} @ ${reply.createdAt}\n`).join("")
Step 4: Build filter functionality with nested predicates
Next, let’s explore the how to use nested query predicates to apply filters based on conditions of a related data. For example, we’ll build a filter functionality to only show blog posts that have replies with a given keyword.
In the sample code base the filter
state is already hooked up to a the <input />
element. All we have to do is edit the useEffect(...)
hook to leverage the nested query predicates.
Go to your App.js file and edit the useEffect
hook to conditionally apply the filter:
useEffect(() => {
let sub;
if (filter) {
sub = DataStore
.observeQuery(Post, p => p.comments.replies.content.contains(filter))
.subscribe((snapshot) => { // ^ see new type improvements here
setPosts(snapshot.items)
})
} else {
sub = DataStore
.observeQuery(Post)
.subscribe((snapshot) => {
setPosts(snapshot.items)
})
}
return () => sub.unsubscribe()
}, [filter])
Now if you go back to your app, you’ll see that once you enter a filter text, you’ll only see blog posts with comment replies that contain the filter text.
Step 5: Deploy latest changes
Now that your app is working as expected, you can commit your latest changes to Git and push them to GitHub. Amplify Hosting is going to automatically kick-off a new build and give you a domain to access your app globally.
git commit -a -m "added filter and export feature"
git push -u origin main
🥳 Success
This demo just scratches the surface of what’s possible with AWS Amplify DataStore. Learn more Amplify DataStore at the AWS Amplify documentation.
As always, feel free to reach out to the Amplify team via GitHub or join our Discord community. Follow @AWSAmplify on Twitter to get the latest updates on feature launches, DX enhancements, and other announcements.
Project clean-up
To remove the backend resources generated from this blog post run the following command in your Terminal:
amplify delete --force
Go to GitHub and delete the amplify-lazy-blog
repository to remove the frontend artifacts created by this tutorial.