Front-End Web & Mobile
AWS AppSync WebSockets-based subscriptions for real-time updates now support nested filtering
AWS AppSync is a fully managed service that enables developers to build digital experiences based on real-time data. With AppSync, you can configure data sources to push and publish real-time data updates to subscribed clients. AppSync handles connection management, scalability, fan-out and broadcasting, allowing you to focus on your application business needs instead of managing complex infrastructure. With AppSync, developers can easily specify filtering rules for their subscriptions to target specific connected clients based on the published data.
Today, AppSync introduces support for filtering on nested schema fields in enhanced subscription filters. With this change, you can now target specific sub-items within the data published in the selection set of you mutations. This gives you additional control over how data should be broadcasted via subscriptions. You can create types in your schema composed of complex fields and create subscriptions that have up to five levels of nesting. You do not have to make changes to you nested schema types, and you can start using this functionality of AppSync’s pub/sub engine right away to implement real-time experiences.
Getting started
To show how you can start using this feature in your own application, we will use a simple schema inspired by the Real-Time Live Sports Updates API. Head to the AppSync console, and create a new AppSync GraphQL API with this schema:
The schema allows you to create game events with the createGameEvent
mutation, and to subscribe to these events with the onCreateGameEvent
subscription. Note that GameEvent
has a player
field that is a complex nested type. The API uses a NONE
resolver that publishes data without saving it to a data source. The createGameEvent
resolver is attached to the NONE resolver and simply forwards the payload locally.
import { util } from '@aws-appsync/utils';
export function request(ctx) {
const { gameId, playerId: id, assists, points } = ctx.args.input;
const payload = {
id: util.autoId(),
createdAt: util.time.nowISO8601(),
game: { id: gameId },
player: { id, stats: { assists, points } },
};
return { payload };
}
export const response = (ctx) => ctx.result;
The subscription onCreateGameEvent
resolver is associated with a NONE
resolver as well.
import { util, extensions } from '@aws-appsync/utils';
export const request = (ctx) => ({ payload: null });
export function response(ctx) {
const { playerId, assists, points } = ctx.args;
const filter = { 'game.id': { eq: ctx.args.gameId } };
const playerFilter = [];
if (playerId) {
playerFilter.push({
'player.id': { eq: playerId },
or: [
{ ...(points ? { 'player.stats.points': { ge: points } } : null) },
{ ...(assists ? { 'player.stats.assists': { ge: assists } } : null) },
],
});
}
filter.or = playerFilter;
console.log('filter:', filter);
extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));
return null;
}
The resolver response handler sets up a subscription that notifies connected clients when a game event for a specific game (gameId
) has happened. Optionally, if the playerId
is provided, the resolver further filters for events that involve the specified player and conditionally filters on assists
and points
. Note the use of extensions.setSubscriptionFilter
to set up the filter, and util.transform.toSubscriptionFilter
to transform the filter to the appropriate format.
You can test your subscription from the AppSync console Query editor. First, establish the subscription. In the example below, you subscribe to an hypothetical NBA game, listening for game events between Dallas and Milwaukee, that involves Luka Doncic where they have at least 30 points or 10 assists.
In your Amazon CloudWatch Logs, you can see the log line with the configured filter that includes the nested rules (logged from the subscription resolver code):
To test the subscription, open another instance of the Query Editor in a new tab, and send a mutation:
You can make changes to the game ID and the involved player to see how and when the subscription is triggered.
Invalidating subscriptions
You can invalidate live subscriptions using nested filters. For example, let’s say you want to close all active subscriptions when a game ends. First, update your onCreateGameEvent
subscription response handler and add the following lines before the return
statement:
const invalidation = { 'game.id': { eq: ctx.args.gameId } };
extensions.setSubscriptionInvalidationFilter(util.transform.toSubscriptionFilter(invalidation));
This creates an invalidation filter in the subscription based on the nested field game.id
. Next, add the endGame
mutation to your schema:
Create a resolver for endGame
with the following code:
import { extensions } from '@aws-appsync/utils';
export function request(ctx) {
return { payload: { id: ctx.args.gameId } };
}
export function response(ctx) {
extensions.invalidateSubscriptions({
subscriptionField: 'onCreateGameEvent',
payload: { 'game.id': ctx.args.gameId },
});
return ctx.result;
}
When the endGame
mutation is called with a game ID, the resolver will send a subscription invalidation request with that specific ID. Stop and restart your subscription. Then, to invalidate a subscription, use the endGame
mutation:
Any subscription that uses an invalidation filter that matches the value is ended and the following message is shown:
Conclusion
In this post, we reviewed the new nested object filtering functionality for AppSync’s enhanced filters. We also showed how you can use nested object filtering to invalidate subscriptions. To learn more about using enhanced filtering in your own subscriptions, see the documentation and the tutorials. You can also find easy-to-use samples and guides in the samples repository.