AWS Logo
Menu
Building Real-Time Notifications with AWS AppSync: From GraphQL to WebSockets

Building Real-Time Notifications with AWS AppSync: From GraphQL to WebSockets

Real-time applications are becoming the norm, from chat systems to live dashboards. Discover how AWS AppSync simplifies the development of real-time solutions using GraphQL subscriptions and WebSockets. Learn the basics of GraphQL, resolvers, and data sources, and follow a practical guide to building a notification system. Ideal for anyone looking to implement efficient, scalable, and secure real-time functionality.

Published Dec 23, 2024

Basic Concepts

Before we dive into implementing a real-time solution with AWS AppSync , it’s important to understand the key concepts behind this technology. Here we explain the essential elements in a clear and simple way:

What is GraphQL?

GraphQL is a query language for APIs that enables clients to request exactly the data they need—no more, no less. Designed to address the limitations of traditional REST APIs, GraphQL offers:
  1. Flexibility : You can get data from multiple resources in a single request.
  2. Efficiency : Avoids problems with “overfetching” (getting more data than needed) or “underfetching” (needing multiple requests to complete a task).
  3. Predictable structure : The client defines how the data will look in the response, which facilitates frontend development.
An example GraphQL query to get task information might look like this:
The response would have the same structure as you defined in the query:

What is a Resolver?

A resolver is the logic that connects your GraphQL schema to the actual data source. In other words, it’s the “bridge” that translates a GraphQL query or mutation into concrete operations in your backend, such as a database query or the execution of a Lambda function.
In AppSync , resolvers are configured using mapping templates written in VTL (Velocity Template Language). For example, a resolver to get tasks from a DynamoDB table might look like this:
This resolver tells AppSync to perform an operation Scanon the DynamoDB table named TasksTable.

What is a Data Source?

A data source is any location from which AppSync can obtain or store data. Some common examples are:
  1. DynamoDB : Ideal for structured and fast-access data.
  2. AWS Lambda : For custom logic or access to external services.
  3. RDS : For relational databases.
  4. HTTP Endpoints : To interact with external APIs.
Each data source is associated with one or more resolvers. For example:
  • A query getTaskscan use DynamoDB as a data source.
  • A mutation createTaskcan use Lambda to execute more complex logic.

What is WebSocket and why is it useful in real-time?

WebSocket is a protocol that allows real-time, two-way communication between client and server. Unlike traditional HTTP requests, where the client must initiate all interactions, WebSocket maintains an open connection that allows the server to send updates to the client at any time.
This makes it perfect for applications such as:
  • Real-time notifications.
  • Online chats.
  • Dashboards with instant updates.
In AppSync , GraphQL subscriptions use WebSockets to notify connected clients when changes occur to data related to their subscription.

Data Collection Strategies: Polling vs WebSockets

Polling

It is a technique in which the client makes periodic requests to the server to check if there are any changes in the data.
Advantages:
  • Easy to implement with standard HTTP requests.
  • Does not require a persistent connection.
Disadvantages:
  • Inefficient : Consumes more client and server resources due to repetitive requests.
  • Latency : Data is only updated at predefined time intervals.

WebSockets

It is a protocol that allows maintaining a bidirectional and persistent connection between the client and the server.
Advantages:
  • Efficiency : Data is sent only when there are changes, reducing the load on the server.
  • Real-time : Allows instant updates without the need for query intervals.
  • Disadvantages:
  • Greater complexity in implementing and managing connections.
  • Requires infrastructure that supports WebSockets.

Key Difference:

With polling , the client constantly “asks” for updates, while with WebSockets , the server “notifies” the client when something relevant happens.

GraphQL vs REST

REST (Representational State Transfer)

It is an architectural style where resources are exposed as endpoints (URLs) and interacted with using HTTP methods such as GET, POST, PUTand DELETE.
Advantages:
  • Standardized and widely adopted.
  • Cacheable: Supports HTTP-based caching mechanisms.
Disadvantages:
  • “Overfetching” and “Underfetching”: The client may receive more data than necessary or require multiple calls to obtain all the required data.
  • Difficulty in evolving: Changing the resource structure can affect multiple clients.

GraphQL

It is a query language that allows clients to define exactly what data they need in a single request.
Advantages:
  • Flexibility : A single query can retrieve data from multiple resources.
  • Evolutionary : Changes to the schema do not break existing customers.
  • Efficiency : Reduces unnecessary requests to the server.
Disadvantages:
  • Higher learning curve for unfamiliar teams.
  • It may be less efficient in simple operations compared to REST.
Key Difference:
  • REST delivers fixed data in a predefined format by the server, while GraphQL allows clients to specify what data they need and in what structure.

What is AWS AppSync?

AWS AppSync is a managed service that makes it easy to build scalable, secure, real-time GraphQL APIs. Its main goal is to simplify the interaction between applications and data sources, providing an all-in-one solution for building modern applications.

Key Features of AppSync

GraphQL Serverless : Run GraphQL APIs without having to manage servers.
AWS Native Data Sources : DynamoDB, Lambda, S3, RDS, among others.
WebSockets and Real-Time Support : Implement GraphQL subscriptions with WebSockets for instant notifications.
Scalability – AppSync automatically scales to handle hundreds or thousands of simultaneous connections.
Amplify Integration : Allows for easy setup and use on web and mobile applications.
Cross-Platform Compatibility : Works with any standard GraphQL client.

Authentication Types

API Key:
  • It is used for development or testing.
  • Less secure, since any client with the key can access the API.
AWS IAM:
  • Requires authentication using AWS roles or users.
  • Ideal for backend integrations or server-to-server services.
Cognito User Pools:
  • Provides user-based authentication.
  • Perfect for applications with end-user registration.
OpenID Connect (OIDC):
  • Allows you to integrate external identity providers.
  • Useful for applications that use custom authentication.
Lambda Authorizer:
  • Execute custom authentication logic in a Lambda function.
  • Ideal for complex scenarios where you need specific rules.

Problem Example

Imagine you are developing a productivity app where users can manage tasks as a team. One of the key requirements is that any team member receives real-time notifications when a new task is created, without the need to manually refresh the app.

For example:

  • If a user creates a new task, all team members should receive an instant notification on their devices, indicating that the task has been created.
  • This includes users connected from multiple platforms (web, mobile, tablet).

Problems with traditional solutions

To implement this functionality, you might consider the following options:
Polling:
Configure the application to query the server periodically for new tasks.
Problem: This creates a high load on the server, is inefficient, and does not provide instant updates (there may be noticeable latency).
REST with Push Notifications:
Using a REST backend along with push notifications to send updates.
Problem: Push notifications depend on external services (such as Firebase for mobile) and are not ideal for web applications or cross-platform scenarios.
Custom WebSockets:
Implement a solution using WebSockets to maintain a persistent connection between client and server.
Problem: Requires managing WebSockets infrastructure, connections, reconnections, and scalability manually, which can be complex and expensive.

Solution with AppSync and WebSockets

AWS AppSync solves this problem elegantly using WebSockets-based GraphQL subscriptions. With this solution:
  1. Persistent Connection: Clients establish a WebSocket connection with AppSync to receive real-time notifications.
  2. Automatic Notifications: When a user creates a new task, AppSync automatically notifies all subscribed clients.
  3. Scalability: AppSync automatically handles scalability and connection management, removing infrastructure complexity.
  4. Cross-platform: Works perfectly on web, mobile, or any GraphQL-compatible client.

Step-by-Step Solution: Real-Time Notifications with AWS AppSync

Below, I walk you through how to implement a real-time notification system using AWS AppSync . Each step includes specific instructions on what to do in the AWS console, how to configure resolvers and data sources, and what code to add:

Step 1: Create a GraphQL API in AWS AppSync

Access the AWS AppSync console:

  • Go to the AppSync consolein AWS.
  • Click the “Create API” button .
  • Select “Build from scratch” and give your API a name, such as TaskManagementAPI.
Step 2: Set up the initial GraphQL schema:
In the “Schema” section , define the following basic schema for handling tasks and notifications:
Initial scheme:

 This scheme defines:
  • A guy Taskwho represents tasks.
  • A mutation createTaskto add new tasks.
  • A subscription onTaskCreatedthat is activated every time you run createTask.
  • A query getTasksto get all tasks (optional).

Step 3: Configure the Data Source (DynamoDB)

Create a DynamoDB table:
  • Go to the DynamoDB console.
  • Click “Create Table” .
  • Assign the name TasksTable.
  • Set the Primary Key to id(String type).
  • Create at the blackboard.
Configure DynamoDB as a Data Source in AppSync:
  • Return to the AppSync console.
  • In the “Data Sources” section, click “Create Data Source” .
  • Select “Amazon DynamoDB Table” .
  • Assign a name, such as TasksDynamoDB.
  • Select the table TasksTableyou created in the previous step.
  • Click “Create” .

Step 4: Configure the Resolvers

Solve forcreateTask
  1. Go to the “Schema” section.
  2. In the mutation createTask, click "Attach Resolver" .
  3. Configure a resolver with the following mapping template (VTL):
Request Mapping Template:
Response Mapping Template:
Solve foronTaskCreated
  1. The subscription does not require a manual resolver. AppSync automatically configures it by linking the mutation createTaskin the schema with @aws_subscribe.
Solve forgetTasks
  1. In the query getTasks, click "Attach Resolver" .
  2. Configure a resolver with the following mapping template:
Request Mapping Template:
Response Mapping Template:
Step 5: Test the API from the Console
  • Go to the “Queries” sectionin the AppSync console.
Run the mutation to create a task:
Set up a subscription in the console:
  • From another browser tab or GraphQL client, run the mutation createTask. You will see the subscription receive the task in real time.
Now let's increase the complexity of the exercise:
  • We will have tasks that are grouped by an identifier
  • When there is a subscription, this subscription must be made by the grouping identifier
  • When a new task is created, if there is an active subscription for that grouper ID, the notification should be seen

Step 6: Update the GraphQL Schema

Modify the GraphQL schema so that the subscription accepts an argument boardId. This argument will allow task notifications to be filtered based on the board.

Key changes:

boardIdin the task: All tasks are associated with a board ( boardId).
boardIdSubscriptionargument : Customers can subscribe only to tasks created on a specific board.
Relationship between createTaskand onTaskCreated: The subscription onTaskCreatedis triggered only when the mutation is executed createTask.

Step 7: Configure the Resolver foronTaskCreated

You don't need to manually configure a resolver for the subscription because AppSync handles this automatically with the annotation @aws_subscribe.
However, AppSync uses internal filters for subscriptions. When a client subscribes, AppSync automatically filters events based on the boardId.

Step 8: We check again the task creation action and the subscription with boardId

Execution of the subscription:
Variables:
We create a new task associating the subscription's boardId:
The result of the creation should be this:
And from where we are making the subscription we should see the generated event:
References:
If you liked this article, don't hesitate to give it a 👏

🤔 Follow me on social media! ⏬

Comments