You are viewing the preview version of this book
Click here for the full version.

Event filtering

Try it yourself

You can find code example for this chapter here.

So far we've covered only how to trigger and consume subscription events. Now we'll look into it how to filter the events. Filtering is essential for two reasons.

First, sending all notifications to all clients does not scale. As the system grows, clients should receive only the events that they are interested in and shouldn't be bothered with everything else.

And second, security. Since notifications contain data, they should be access controlled just like normal queries. Imagine everybody getting all the Facebook messages somebody sends. That would expose every single chat conducted on that platform. Notifications almost always contain sensitive data which means it's important to send them only who should have access to the data.

In the case of AppSync, imagine that all notification events go into a big bucket and filtering determines what to send to whom. This is a bit different than traditional real-time updates where you manage the connections as you can target individual clients. Here, there is no targeting but only filters, determined when clients subscribe.

AppSync sends the subscription event to clients based on filters

AppSync provides two ways to filter messages. The original, arguments-based filtering and a newer addition, called enhanced filtering. The latter is more powerful but also way more complicated and in my experience it is useful in only a couple of cases. This means, the majority of subscriptions will likely use arguments-based filtering, that's why we'll cover both ways.

Subscription filters

Subscription filters are determined when a client sends the subscription request. These is no way to change the filters for already subscribed clients.

Arguments-based filtering

The easiest way to define which events go to a client is to use subscription arguments. This way integrates well into the GraphQL world as it reuses a feature that already exists: field arguments.

For example, let's add a subscription with a name argument:

type Subscription {
  newTodo_basic(name: String): Todo
  @aws_subscribe(mutations: ["addTodo"])
}

As we've previously discussed, this subscription will be triggered when the addTodo mutation is called, as defined in the @aws_subscribe directive.

With the above subscription in the schema, clients can define the name of the Todo items they are interested in:

subscription MySubscription {
  newTodo_basic(name: "Shopping") {
    created
    id
    name
  }
}

Then a mutation triggers this event:

mutation MyMutation {
  addTodo(name: "Shopping", userId: "user1", severity: MEDIUM) {
    id
    created
    name
    severity
  }
}

This is because the return value has a name field with the value "Shopping".

The subscription argument matches the subscrition event

To check a negative example, send a mutation with a different name and observe there is no subscription event generated:

mutation MyMutation {
  addTodo(name: "Work", userId: "user1", severity: MEDIUM) {
    id
    created
    name
    severity
  }
}
The subscription argument does not match the subscription event
Only top-level fields

A huge limitation of AppSync subscription is that filters only match the top-level fields. In the above case, clients can define the name of the Todo items they are interested in as that is a top-level field, but they can't filter by the user attached to the item. Because of this, you need to move everything you want to filter on to the top level. We'll see an efficient way to do that in the Notify subscription pattern chapter.

Arguments can define multiple fields in a subscription event and all of them needs to match. For example, let's add another subscription with also a severity argument:

enum Severity {
  LOW
  MEDIUM
  HIGH
}

type Todo {
  id: ID!
  name: String!
  created: AWSDateTime!
  user: User!
  # new field
  severity: Severity!
}

type Subscription {
  newTodo_basic_with_severity(name: String, severity: Severity): Todo
  @aws_subscribe(mutations: ["addTodo"])
}

With these two arguments both optional, clients can filter by name, severity, both, or none of them. And when the arguments are present, they require strict equality.

This subscription gets all "Shopping" tickets no matter the severity:

subscription MySubscription {
  newTodo_basic_with_severity(name: "Shopping") {
    created
    id
    name
  }
}

This gets only high-priority ones:

subscription MySubscription {
  newTodo_basic_with_severity(severity: HIGH) {
    created
    id
    name
  }
}

And this one gets only high-priority shopping tickets:

subscription MySubscription {
  newTodo_basic_with_severity(name: "Shopping", severity: HIGH) {
    created
    id
    name
  }
}

Note that what filters are possible is defined in the schema, but the filters for the specific subscription is defined by the client. In the last example, if the client does not define either arguments, they get all events. When using arguments-based filtering, the only way to make sure events don't go to unintended clients is to reject the subscription. We'll look into it in the Access control for subscriptions chapter.

Only the fields in the mutation will be considered for filters

AppSync includes only the fields defined by the mutation in the subscription event. Because of this, if a subscription argument includes a field but the mutation does not, then that event won't be matched.

This mutation won't trigger an event for subscription that define the severity argument as the subscription event does not contain a severity field:

mutation MyMutation {
  addTodo(name: "Shopping", userId: "user1", severity: MEDIUM) {
    id
    created
    name
  }
}

Enhanced filtering

(docs)

AppSync recently added a new filtering mechanism called enhanced subscription filtering. This gives more control to the backend to define what events are sent to the clients.

Enhanced filtering adds support for three things.

First, the resolver implementation defines which fields to filter on instead of the client query.

There is more, but you've reached the end of this preview
Read this and all other chapters in full and get lifetime access to:
  • all future updates
  • full web-based access
  • PDF and Epub versions