graphqlaws-appsyncappsync-apollo-client

AWS AppSync: pass arguments from parent resolver to children


In AWS AppSync, arguments send on the main query don't seem to be forwarded to all children resolvers.

type Query {
  article(id: String!, consistentRead: Boolean): Article
  book(id: String!, consistentRead: Boolean): Book
}

type Article {
  title: String!
  id: String!
}

type Book {
  articleIds: [String]!
  articles: [Article]!
  id: String!
}

when I call:

query GetBook {
  book(id: 123, consistentRead: true) {
    articles {
      title
    }
  }
}

the first query to get the book receives the consistentRead param in $context.arguments, but the subsequent query to retrieve the article does not. ($context.arguments is empty)

I also tried articles(consistentRead: Boolean): [Article]! inside book but no luck.

Does anyone know if it's possible in AppSync to pass arguments to all queries part of the same request?


Solution

  • It is possible to pass arguments from parent to child via the response. Let me explain ...

    AppSync has several containers inside $context:

    arguments and stash are always cleared before invoking a child resolver as evident from these CloudWatch logs:

    At the very end of the parent execution - arguments and stash data are present.

    {
        "errors": [],
        "mappingTemplateType": "After Mapping",
        "path": "[getLatestDeviceState]",
        "resolverArn": "arn:aws:appsync:us-east-1:xxx:apis/yyy/types/Query/fields/getLatestDeviceState",
        "context": {
            "arguments": {
                "device": "ddddd"
            },
            "prev": {
                "result": {
                    "items": [{
                        "version": "849",
                        "device": "ddddd",
                        "timestamp": "2019-01-29T12:18:34.504+13:00"
                    }]
                }
            },
            "stash": {"testKey": "testValue"},
            "outErrors": []
        },
        "fieldInError": false
    }
    

    and then at the very beginning of the child resolver - arguments and stash are always blank.

    {
        "errors": [],
        "mappingTemplateType": "Before Mapping",
        "path": "[getLatestDeviceState, media]",
        "resolverArn": "arn:aws:appsync:us-east-1:yyy:apis/xxx/types/DeviceStatePRODConnection/fields/media",
        "context": {
            "arguments": {},
            "source": {
                "items": [{
                    "version": "849",
                    "device": "ddddd",
                    "timestamp": "2019-01-29T12:18:34.504+13:00"
                }]
            },
            "stash": {},
            "outErrors": []
        },
        "fieldInError": false
    }
    

    Workaround 1 - get the argument from the previous result.

    In the example above device is always present in the response of the parent resolver, so I inserted

    #set($device = $util.defaultIfNullOrBlank($ctx.args.device, $ctx.source.items[0].device))
    

    into the request mapping template of the child resolver. It will try to get the ID it needs from the arguments and then fall back onto the previous result.

    Workaround 2 - add the argument to the parent response

    Modify your parent resolver response template to include the arguments:

    {
        "items": $utils.toJson($context.result.items),
        "device": "${ctx.args.device}"
    }
    

    and then retrieve it in the request mapping template of the child the same way as in the first workaround.