Front-End Web & Mobile
Offline caching with AWS Amplify, Tanstack, AppSync and MongoDB Atlas
In this blog we demonstrate how to create an offline-first application with optimistic UI using AWS Amplify, AWS AppSync, and MongoDB Atlas. Developers design offline first applications to work without requiring an active internet connection. Optimistic UI then builds on top of the offline first approach by updating the UI with expected data changes, without depending on a response from the server. This approach typically utilizes a local cache strategy.
Applications that use offline first with optimistic UI provide a number of improvements for users. These include reducing the need to implement loading screens, better performance due to faster data access, reliability of data when an application is offline, and cost efficiency. While implementing offline capabilities manually can take sizable effort, you can use tools that simplify the process.
We provide a sample to-do application that renders results of MongoDB Atlas CRUD operations immediately on the UI before the request roundtrip has completed, improving the user experience. In other words, we implement optimistic UI that makes it easy to render loading and error states, while allowing developers to rollback changes on the UI when API calls are unsuccessful. The implementation leverages TanStack Query to handle the optimistic UI updates along with AWS Amplify. The diagram in Figure 1 illustrates the interaction between the UI and the backend.
TanStack Query is an asynchronous state management library for TypeScript/JavaScript, React, Solid, Vue, Svelte, and Angular. It simplifies fetching, caching, synchronizing, and updating server state in web applications. By leveraging TanStack Query’s caching mechanisms, the app ensures data availability even without an active network connection. AWS Amplify streamlines the development process, while AWS AppSync provides a robust GraphQL API layer, and MongoDB Atlas offers a scalable database solution. This integration showcases how TanStack Query’s offline caching can be effectively utilized within a full-stack application architecture.
Figure 1. Interaction Diagram
The sample application implements a classic to-do functionality and the exact app architecture is shown in Figure 2. The stack consists of:
- MongoDB Atlas for database services.
- AWS Amplify the full-stack application framework.
- AWS AppSync for GraphQL API management.
- AWS Lambda Resolver for serverless computing.
- Amazon Cognito for user management and authentication.
Figure 2. Architecture
Deploy the Application
To deploy the app in your AWS account, follow the steps below. Once deployed you can create a user, authenticate yourself, and create to-do entries – see Figure 8.
Set up the MongoDB Atlas cluster
- Follow the link to the setup the MongoDB Atlas cluster, Database , User and Network access
- Set up the user
Clone the GitHub Repository
- Clone the sample application with the following command
git clone https://github.com/mongodb-partners/amplify-mongodb-tanstack-offline
Setup the AWS CLI credentials (optional if you need to debug your application locally)
- If you would like to test the application locally using a sandbox environment, you can setup temporary AWS credentials locally:
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
export AWS_SESSION_TOKEN=
Deploy the Todo Application in AWS Amplify
- Open the AWS Amplify console and Select the Github Option
Figure 3. Select Github option
2. Configure the GitHub Repository
Figure 4. Configure repository permissions
3. Select the GitHub Repository and click Next
Figure 5. Select repository and branch
4. Set all other options to default and deploy
Figure 6. Deploy application
Configure the Environment Variables
Configure the Environment variables after the successful deployment
Figure 7. Configure environment variables
Open the application and test
Open the application through the URL provided and test the application.
Figure 8. Sample todo entries
MongoDB Atlas Output
Figure 9. Data in Mongo
Review the Application
Now that the application is deployed, let’s discuss what happens under the hood and what was configured for us. We utilized Amplify’s git-based workflow to host our full-stack, serverless web application with continuous deployment. Amplify supports various frameworks, including server side rendered (SSR) frameworks like Next.js and Nuxt, single page application (SPA) frameworks like React and Angular, and static site generators (SSG) like Gatsby and Hugo. In this case, we deployed a SPA React based application. We can include feature branches, custom domains, pull request previews, end-to-end testing, and redirects/rewrites. Amplify Hosting provides a Git-based workflow enables atomic deployments ensuring that updates are only applied after the entire deployment is complete.
To deploy our application we used AWS Amplify Gen 2, which is a tool designed to simplify the development and deployment of full-stack applications using TypeScript. It leverages the AWS Cloud Development Kit (CDK) to manage cloud resources, ensuring scalability and ease of use.
Before we conclude, it is important to understand our application’s updates concurrency. We implemented a simple optimistic first-come first-served conflict resolution mechanism. The MongoDB Atlas cluster persists updates in the order it receives them. In case of conflicting updates, the latest arriving update will override previous updates. This mechanism works well in applications where update conflicts are rare. It is important to evaluate how this may or may not suit your production needs, requiring more sophisticated approaches. TanStack provides capabilities for more complex mechanisms to handle various connectivity scenarios. By default, TanStack Query provides an “online” network mode, where Queries and Mutations will not be triggered unless you have network connection. If a query runs because you are online, but you go offline while the fetch is still happening, TanStack Query will also pause the retry mechanism. Paused queries will then continue to run once you re-gain network connection. In order to optimistically update the UI with new or changed values, we can also update the local cache with what we expect the response to be. This is approach works well together with TanStack’s “online” network mode, where if the application has no network connectivity, the mutations will not fire, but will be added to the queue, but our local cache can be used to update the UI. Below is a key example of how our sample application optimistically updates the UI with the expected mutation.
const createMutation = useMutation({
mutationFn: async (input: { content: string }) => {
// Use the Amplify client to make the request to AppSync
const { data } = await amplifyClient.mutations.addTodo(input);
return data;
},
// When mutate is called:
onMutate: async (newTodo) => {
// Cancel any outgoing refetches
// so they don't overwrite our optimistic update
await tanstackClient.cancelQueries({ queryKey: ["listTodo"] });
// Snapshot the previous value
const previousTodoList = tanstackClient.getQueryData(["listTodo"]);
// Optimistically update to the new value
if (previousTodoList) {
tanstackClient.setQueryData(["listTodo"], (old: Todo[]) => [
...old,
newTodo,
]);
}
// Return a context object with the snapshotted value
return { previousTodoList };
},
// If the mutation fails,
// use the context returned from onMutate to rollback
onError: (err, newTodo, context) => {
console.error("Error saving record:", err, newTodo);
if (context?.previousTodoList) {
tanstackClient.setQueryData(["listTodo"], context.previousTodoList);
}
},
// Always refetch after error or success:
onSettled: () => {
tanstackClient.invalidateQueries({ queryKey: ["listTodo"] });
},
onSuccess: () => {
tanstackClient.invalidateQueries({ queryKey: ["listTodo"] });
},
});
We welcome any PRs implementing additional conflict resolution strategies.
- Try out MongoDB Atlas on AWS MarketPlace
- Get familiar with AWS Amplify, Amplify Gen2 and AppSync
- For detailed instructions on deploying the application, refer to the deployment section of our documentation
- Submit a PR with your enhancements