Add custom real-time subscriptions
Create a custom real-time subscription for any mutation to enable PubSub use cases.
Define a custom subscription
For every custom subscription, you need to set:
- the mutation(s) that should trigger a subscription event,
- a return type that matches the subscribed mutations' return type,
- authorization rules.
Optionally, you can set filter arguments to customize the server-side subscription filter rules.
Use a.subscription()
to define your custom subscription in your amplify/data/resource.ts file:
1import { type ClientSchema, a, defineData } from '@aws-amplify/backend';2
3const schema = a.schema({4 // Message type that's used for this PubSub sample5 Message: a.customType({6 content: a.string().required(),7 channelName: a.string().required()8 }),9
10 // Message publish mutation11 publish: a.mutation()12 .arguments({13 channelName: a.string().required(),14 content: a.string().required()15 })16 .returns(a.ref('Message'))17 .handler(a.handler.custom({ entry: './publish.js' }))18 .authorization(allow => [allow.publicApiKey()]),19
28
29 // A data model to manage channels30 Channel: a.model({31 name: a.string(),32 }).authorization(allow => [allow.publicApiKey()]),33});34
35export type Schema = ClientSchema<typeof schema>;36
37export const data = defineData({38 schema39});
For this example, we're building a generic PubSub capability. This requires us to convert the arguments for publish
into the Channel
's format. Create a new publish.js
file in your amplify/data/ folder with the following contents:
1// This handler simply passes through the arguments of the mutation through as the result2export function request() {3 return {}4}5
6/**7 * @param {import('@aws-appsync/utils').Context} ctx8 */9export function response(ctx) {10 return ctx.args11}
Next, create a new receive.js
file in your amplify/data/ folder to define handlers for your subscription. In this case, it'll just be a simple passthrough. In the next section, we'll explore how to use this handler to construct more advanced subscription filters.
1export function request() {2 return {};3}4
5export const response = (ctx) => {6 return ctx.result;7};
Subscribe to custom subscriptions client-side
From your generated Data client, you can find all your custom subscriptions under client.subscriptions
. Subscribe using the .subscribe()
function and then use the next
function to handle incoming events.
1import { generateClient } from 'aws-amplify/data'2import type { Schema } from '../amplify/data/resource'3
4const client = generateClient<Schema>()5
6const sub = client.subscriptions.receive()7 .subscribe({8 next: event => {9 console.log(event)10 }11 }12)
You can try publishing an event using the custom mutation to test the real-time subscription.
1client.mutations.publish({2 channelName: "world",3 content: "My first message!"4})
Your subscription event should be received and logs the payload into your app's developer console. Unsubscribe your subscription to disconnect using the unsubscribe()
function.
1sub.unsubscribe()
(Optionally) Add server-side subscription filters
You can add subscription filters by adding arguments to the custom subscriptions.
If you want to customize the filters, modify the subscription handler. For this example, we'll allow a customer to pass in a namePrefix
parameter that allows the end users to only receive channel events in channels that start with the namePrefix
.
1import { type ClientSchema, a, defineData } from '@aws-amplify/backend';2
3const schema = a.schema({4 Channel: a.model({5 name: a.string(),6 }).authorization(allow => [allow.publicApiKey()]),7
8 Message: a.customType({9 content: a.string().required(),10 channelName: a.string().required()11 }),12
13 publish: a.mutation()14 .arguments({15 channelName: a.string().required(),16 content: a.string().required()17 })18 .returns(a.ref('Message'))19 .handler(a.handler.custom({ entry: './publish.js' }))20 .authorization(allow => [allow.publicApiKey()]),21
22 receive: a.subscription()23 .for(a.ref('publish'))25 .handler(a.handler.custom({entry: './receive.js'}))26 .authorization(allow => [allow.publicApiKey()])27});28
29export type Schema = ClientSchema<typeof schema>;30
31export const data = defineData({32 schema33});
In your handler, you can set custom subscription filters based on arguments passed into the custom subscription. For this example, create a new receive.js file alongside the amplify/data/resource.ts file:
1import { util, extensions } from "@aws-appsync/utils"2
3// Subscription handlers must return a `null` payload on the request4export function request() { return { payload: null } }5
6/**7 * @param {import('@aws-appsync/utils').Context} ctx8 */9export function response(ctx) {10 const filter = {11 channelName: {12 beginsWith: ctx.args.namePrefix13 }14 }15
16 extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter))17
18 return null19}