amplify-swift icon indicating copy to clipboard operation
amplify-swift copied to clipboard

Subscription - server side filtering

Open aneaga opened this issue 5 months ago • 7 comments

I have reviewed:

  • https://docs.amplify.aws/swift/build-a-backend/data/custom-subscription/
  • https://docs.amplify.aws/swift/build-a-backend/data/subscribe-data/

Also pulled the full Github repo looking for examples but have found none.

Would much appreciate a full example of configuring and using server-side filtering with a subscription on most simple model type, in my case defined as

 Event: a
    .model({
        familyId: a.id().required(),
        generatedByMemberId: a.id(),
        generatedBy: a.belongsTo('FamilyMember', 'generatedByMemberId'),
        type: a.ref('EventType').required(),
        metadata: a.string().required(),
        family: a.belongsTo('Family', 'familyId'),
    })

In my case, I need filtering by familyId.

Thank you.

aneaga avatar Jul 02 '25 11:07 aneaga

And regarding https://docs.amplify.aws/swift/build-a-backend/data/custom-subscription/, I applied the customType, mutation and subscription, as well as the receive.js and publish.js files. All deployed fine. However, even though the docs should be on Swift, the code examples are not Swift and I could not find a way to actually use the new subscription/publish/receive in Swift.

Also, once my changes have been deployed, I did not see any additional Lambda deployed for receive/publish. Would appreciate an explanation on how exactly the filtering works on server-side.

This is a major blocker for me atm.

My receive.js looks like this atm

import { util, extensions } from "@aws-appsync/utils"

// Subscription handlers must return a `null` payload on the request
export function request() { return { payload: null } }

/**
 * @param {import('@aws-appsync/utils').Context} ctx
 */
export function response(ctx) {
  const filter = {
    familyId: {
      beginsWith: ctx.args.familyId
    }
  }

  extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter))

  return null
}

and the I added the following in data/resource

Message: a.customType({
      familyId: a.string().required(),
      eventName: a.string().required()
    }),
    
    // Message publish mutation
    publish: a.mutation()
      .arguments({
        familyId: a.string().required(),
        eventName: a.string().required()
      })
      .returns(a.ref('Message'))
      .handler(a.handler.custom({ entry: './publish.js' }))
      .authorization(allow => [allow.authenticated()]),

    // Subscribe to incoming messages
    receive: a.subscription()
      // subscribes to the 'publish' mutation
      .for(a.ref('publish'))
      .arguments({ familyId: a.string() })
      // subscription handler to set custom filters
      .handler(a.handler.custom({entry: './receive.js'}))
      // authorization rules as to who can subscribe to the data
      .authorization(allow => [allow.authenticated()]),

aneaga avatar Jul 03 '25 07:07 aneaga

Thanks for your question @aneaga. Let me check with the team and get back to you soon.

mattcreaser avatar Jul 03 '25 13:07 mattcreaser

Hi @aneaga unfortunately we don't support server-side filtering from the Swift library at this time. I'll mark this issue as a feature request and also open an issue to clean up that part of the documentation.

mattcreaser avatar Jul 07 '25 13:07 mattcreaser

This has been identified as a feature request. If this feature is important to you, we strongly encourage you to give a 👍 reaction on the request. This helps us prioritize new features most important to you. Thank you!

github-actions[bot] avatar Jul 07 '25 13:07 github-actions[bot]

I was testing a less complex use case than you have described, but while we do not provide easy support of subscription server side filtering, it may be possible with custom requests such as this:

let document = """
subscription OnCreateEventBeginsWithName($name: String!) {
  onCreateEvent(filter: {name: {beginsWith: $name}}) {
    id
    name
  }
}
"""
let variables = ["name": name]
let request = GraphQLRequest<Event>(document: document, variables: variables, responseType: Event.self)
let subscription = Amplify.API.subscribe(request: request)

tylerjroach avatar Jul 09 '25 20:07 tylerjroach

@tylerjroach Thanks, but I was not able to have it work, even without any filtering criteria. I believe that may work with Gen1, but I run Gen2.

Also tried the following

let filter = Event.keys.family.eq(familyId)
let request = GraphQLRequest<[Event]>.list(Event.self, where: filter)
let subscription = Amplify.API.subscribe(request: request)

which fails with

GraphQLResponseError<List<Event>>: GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "query not supported through the realtime channel", locations: nil, path: nil, extensions: Optional(["errorType": Amplify.JSONValue.string("UnsupportedOperation")]))]

aneaga avatar Jul 10 '25 10:07 aneaga

hi @aneaga From the client side perspective, there is no difference between a gen1 or gen2 backend. If you have the subscription filtering configured on one platform that is working, you should be able to capture the raw graphql document and variables, and replicate the experience. If you want to post any errors you are seeing, let me know.

tylerjroach avatar Jul 10 '25 12:07 tylerjroach