GraphQL Serverless Real-Time Reference Architecture with AWS AppSync
In our busy and fast-paced modern society, mobile and web application users demand quick access to information and data as soon as it happens —whenever there’s any update or change, no matter where they are. They also want data to show up automatically on their screens as soon as there’s a request sent by another user or by the backend itself, with no need to reload or refresh their client app. Real-time use cases are getting increasingly more common, and will soon become a hard requirement for any sort of engaging and collaborative application.
However, real-time at scale is a hard problem to solve. How do you manage live connections, fan-out capabilities, and seamless performance as the number of connected clients increase? It’s a big challenge for any application development team to build and manage a reliable back-end system that can address all of these important aspects. What if it was possible to build and deploy scalable real-time applications with no need to worry about back-end servers, connection management, fan-out, high availability, reliability—as well as take advantage of a highly optimized and robust GraphQL data layer to deliver your application data to your front-end app users, allowing you to spend more time focusing on your business logic instead?
AWS AppSync is a GraphQL serverless backend for mobile, web, and enterprise applications that provides a flexible, smart, and reliable data layer to access multiple data sources in your AWS account.
AWS AppSync takes advantage of GraphQL subscriptions to perform real-time operations by pushing data to clients that choose to listen to specific events from the backend. This means that you can easily and effortlessly make any supported data source in AWS AppSync real time, with connection management handled automatically by the AWS AppSync client SDK or AWS Amplify client using WebSocket as the network protocol between the client and service :
AWS takes care of the undifferentiated heavy lifting of managing your real-time back-end infrastructure. However, it’s important that your application is well architected and designed in order to provide the best experience possible to your end users. For that reason, today we’re publishing our first AWS AppSync real-time reference architecture, with sample code that will make it easier to understand and apply best practices to your next collaborative real-time application development project.
Our reference architecture showcases different types of real-time data broadcasting using GraphQL subscriptions over WebSocket:
- Back-end broadcasting: From a serverless backend to all clients (one-to-many)
- Client broadcasting: Between multiple clients (many-to-many)
With different types of backends:
- Amazon DynamoDB (NoSQL serverless database)
- AWS Lambda (Serverless compute logic)
- AWS AppSync Local Resolvers (Publish/Subscribe only, data isn’t persisted)
The sample app is based on second screen kind of experiences where you usually have something getting broadcasted to all connected users, who in turn can interact with each other, as well as see the activity of other users in real time. We have a single movie poster and description shared with all clients for a short amount of time before it’s all rotated. Users can vote on how they feel about the current movie, as well as express their opinion in a public chat room. The most voted movies at any specific time in different categories are displayed on a leader board.
How does it work behind the scenes?
Every 10 seconds, Lambda calls an external source (The Movie DB) to retrieve a single random item from the popular movies category. After the movie information is retrieved, Lambda then sends a call (mutation) to AWS AppSync with the movie data, which is then stored in a DynamoDB “Movie” table. All clients are subscribed to the specific GraphQL mutation that changes the movie in DynamoDB. They all get the information related to the current movie at the same time. Every 10 seconds, all clients see the exact same movie data displayed on their screen:
Clients are able to vote on the current movie during the 10 seconds when the movie is displayed on the screen. Current voting activity and counters are shared in real time to all users as soon as they happen. Votes are saved in a DynamoDB “Reviews” table, and numbers are updated using an atomic counter in the backend. Whenever Lambda sends a mutation for a new movie, it sends a second mutation to clear all votes in the Reviews table as well. It also calculates and stores the top votes in different categories for specific movies. The top votes are automatically displayed in a leaderboard and broadcast to all users as soon as they’re updated by Lambda.
Finally, we also have a public chat room where users can send messages and communicate with each other. Since we’re using a local resolver in AWS AppSync, chat messages are ephemeral and aren’t persisted or stored anywhere. New users won’t be able to see past messages. However, all connected clients will be able to retrieve new messages, as well as share their opinion in real time after they connect to the app.
What about security? Our users don’t need to authenticate because they have public access to the app. However, our AWS AppSync GraphQL API itself is secured with IAM authorization on the backend. An Amazon Cognito Identity Pool with an unauthenticated role is used to grant access to end users, which also enables you to keep track of how many users are connected using identity IDs in the pool. Lambda also uses IAM to call AWS AppSync from the backend with an HTTP client that makes POST calls signed with AWS Signature v4 to make secure GraphQL mutations. Everything is secured end to end — a malicious third party won’t be able to tamper, modify, or access data.
It’s also important to optimize the way your app works and the amount of connections you send to the backend—work smarter, not harder. In our specific sample app case, we don’t need every single vote to be a mutation to the backend. The app was developed so that the first vote in any category triggers a call to AWS AppSync. However, if a user really loves a movie and clicks the voting button several times, let’s say 5, it’s not optimal to make 5 different calls just to record 5 votes in our DynamoDB table. Remember, you have to pay for read/write capacity. There’s a process in our app code that runs every couple of seconds, aggregates subsequent votes, and adds them to the voting total for that category. So, in our example, instead of 5 calls, we would be making only 2 mutation calls (the first vote and the next 4 aggregated votes).
The sample app is now available on GitHub. You can one-click deploy to the AWS Amplify Console and have it up and running in your own AWS account in minutes:
AWS AppSync provides a flexible and powerful GraphQL data layer that makes it easier to deliver data to your web and mobile applications in real time. It provides a robust, secure, highly available serverless backend that’s capable of handling millions of events a day. Best of all, you don’t need to worry about managing the complexities of a real-time backend infrastructure, connections management, capacity, fan-out capabilities, and scalability. This gives you more time to spend on what’s important for your business, application, and users.