aws-appsync-community icon indicating copy to clipboard operation
aws-appsync-community copied to clipboard

Change subscription mapping to match what is returned from the resolver, not what is requested by the user

Open atlowell-smpl opened this issue 4 years ago • 7 comments

I'm tempted to label this as a bug, considering how bizarre this behavior is to me, but you can consider this a feature request.

Consider the following example:

type Event {
    id: String!
    name: String!
    description: String
}

type mutation {
    createEvent(name: String!, description: String): Event
}

type subscription {
    onCreateEvent(name: String!): Event @aws_subscribe(mutations: ["createEvent"])
}

A user can create an event, specifying a name and, optionally, a description. An id is auto-generated for each event in the createEvent resolver.

Suppose Alice wants to subscribe to events that are created with the name "Picnic". She can submit a subscription:

subscription SubscribeToPicnics {
    onCreateEvent(name: "Picnic") {
        id
        name
        description
    }
}

Suppose Bob goes to create an even with the name picnic. If he uses the following mutation:

mutation CreatePicnic {
    createEvent(name: "Picnic", description: "A nice afternoon on the hill") {
        id
        name
        description
    }
}

Then Alice will receive a response that looks like:

onCreateEvent: {
    "id": "12345",
    "name": "Picnic",
    "description": "A nice afternoon on the hill",
    "__typename": "Event"
}

HOWEVER, suppose Bob instead sends a mutation like this:

mutation CreatePicnic {
    createEvent(name: "Picnic", description: "A nice afternoon on the hill") {
        id
        name
    }
}

Then Alice's response will be missing the description field, despite it being returned by the resolver:

onCreateEvent: {
    "id": "12345",
    "name": "Picnic",
    "__typename": "Event"
}

Even worse, if Bob sends a mutation like this (perhaps because he doesn't need the data, he just wants to confirm it has been created):

mutation CreatePicnic {
    createEvent(name: "Picnic", description: "A nice afternoon on the hill") {
        id
    }
}

Then Alice will never receive the real time data of the created event.

Herein lies the problem: Subscription matching as implemented is based on what the CLIENT REQUESTS, rather than what the RESOLVER RETURNS. As a result, the integrity of the subscription system relies on the client. Other than the obnoxious implications for development, this could prove a security risk in certain situations, as it gives the client control over whether a subscription is sent.

atlowell-smpl avatar Mar 16 '20 00:03 atlowell-smpl

@atlowell-smpl It's definitely a feature request because the AppSync documentation says:

the client triggering the mutation specifies the selection set that subscribers receive

Multiple people at AWS have also confirmed it's designed to work this way.

buggy avatar Mar 16 '20 02:03 buggy

While it may be ‘designed that way’, it definitely feels like unexpected behaviour from an end user perspective. I hit this same issue the other day and was super confused why I was getting null values back for the fields I was requesting.

0xdevalias avatar Apr 17 '20 00:04 0xdevalias

I understand that there could be some technical challenges for this issue (or feature) but it is really bad behavior for users. I am not sure what could be the use case for mutation where the client wants to get all information including which is set by the same client in the same mutation operation. And if the client does not do this then other clients will not get an update for this mutation or will not get all information for mutation over subscription. Please update this behavior if possible.

codegame6 avatar Oct 05 '21 23:10 codegame6

Agreed! I don't understand why this issue is having such little attention, I hit the exact same problem as @codegame6 - subscribers should define what they want from what data filed they want, this should NOT defined by mutation.

Suppose I have a new field added to the model, it is natural that you will go to the subscription and update the requested response data field, not got to the mutation because I don't care the data result of a mutation since I only want to mutate it.

There could be backward compatibility problems as well.

YuantongL avatar Mar 28 '22 02:03 YuantongL

Any update on this issue / feature request, has this been added to the roadmap? As mentioned already in previous comments, this seems unexpected and good thing I read about this behavior in the Lessons learned: AWS AppSync Subscriptions post.

buholzer avatar Dec 13 '23 10:12 buholzer

Hey, thanks for checking in on this issue.

This is the intended behavior. Essentially, we are making the result of the subscription (the output as specified in the selection set) available to the subscription, not the input (the mutation arguments).

Also, in a lot of cases the input of the mutation is not the same as the output. For example, in your description of the issue, there is an id returned in the mutation selection set that was not provided in the input.

Also keep in mind that we now support 2 methods to "filter" data: one that allows clients to specify filters directly in the subscription arguments, and another that allows the server (the AppSync service) to control how messages are filters (and who receives messages) using enhanced filtering. With enhance filtering, you write your filters in your resolver and the client filters are not applied. See: https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-enhanced-filtering.html

This however does not impact the fundamental behavior: The data available to the subscription is the set (or subset) of the selection set returned as the result of the mutation.

onlybakam avatar Dec 14 '23 01:12 onlybakam

Thanks for the reply @onlybakam, much appreciated.

It would be great if AppSync provided an option for a 'subscription event resolver' that allows to expand/manipulate the data before it is handed over to the subscribers.

In my use case I have a Subscription with a union return type and if the corresponding mutation is not requesting the __typename AppSync will complain that it does not know what type of the Union it should return.

With this behavior we are creating a tight dependency between the different actors which is hard to control especially if not all clients are implemented by the same team or organization (e.g. public facing GraphQL API).

buholzer avatar Dec 14 '23 11:12 buholzer