AWS AppSync: pass arguments from parent resolver to children

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

AppSync has several containers inside $context:

  • arguments
  • stash
  • source

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.


To achieve availability across all related resolvers (nested or those collection-entity related) for me was fine Workaround 2 (tnx Max for such a good answer) but just for child resolvers. In another case when I needed to resolve entities from collection query (contains other fields besides entity) property added to response mapping template wasn't available anymore. So my solution was to set it to request headers:

##Set parent query profile parameter to headers to achieve availability accross related resolvers.
#set( $headers = $context.request.headers )
$util.qr($headers.put("profile", $util.defaultIfNullOrBlank($context.args.profile, "default")))

And read this value from your nested/other request mapping templates:

#set($profile = $ctx.request.headers.profile)

This makes the parent argument available wherever I need it between related resolvers. In your case, it would be 'device' and some default value or without that part if not needed.