I'm successfully using a pipeline resolver to persist a parent/child relationship, except when the list of child items is empty and I #return
early.
I'm guessing the issue is around my response mappers and use of $ctx.prev
vs $ctx.result
but I can't figure it out.
The pipeline looks like this:
{}
PutItem
the parent$utils.toJson($ctx.result)
TransactWriteItems
(foreach UpdateItem
) the children$utils.toJson($ctx.prev.result)
$utils.toJson($ctx.prev.result)
When I call the mutation with
{"parentAttribute":"foo", "children": [{"childAttribute": "bar"}]}
I get a good response like:
{
"data": {
"createFoo": {
"parentAttribute": "foo",
"children": [
{
"childAttribute": "bar"
}
]
}
}
}
If no children, Function 2 request mapper does #return
to avoid "TransactWriteItems must have at least one operation" error.
In this scenario I am hoping for the above response to the mutation, just with children: []
Instead, I get:
{
"data": {
"createFoo": null
}
}
The data has been written correctly; if I query it I get back the parent with empty list of children.
How do I get this pipeline to execute so that it returns the combined parent+child data whether the child array is populated or not?
The schema is something like:
type Foo {
id: String!
attr1: String
bars: [Bar]
}
type Bar {
id: String!
attr2: String
}
type Mutation {
createFoo(foo: Foo): Foo
}
And a dynamodb representation like this:
pk | sk | attr1 | attr2 |
---|---|---|---|
FOO#1 | METADATA#FOO#1 | Lorem | |
FOO#1 | BAR#1 | Ipsum |
While the pipeline looks like:
before.vtl
{}
createParent-request.vtl
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
"pk" : $util.dynamodb.toDynamoDBJson(...),
"sk" : $util.dynamodb.toDynamoDBJson(...)
},
"attributeValues" : {
"data" : $util.dynamodb.toDynamoDBJson(...)
}
}
createParent-response.vtl
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($ctx.result)
createChildren-request.vtl
#if($ctx.args.fooInput.children.size() > 0)
{
"version": "2018-05-29",
"operation": "TransactWriteItems",
"transactItems": [
#foreach( $child in $ctx.args.fooInput.children )
{
"table": "${table}",
"operation": "UpdateItem",
"key": {
"pk" : $util.dynamodb.toDynamoDBJson(...),
"sk" : $util.dynamodb.toDynamoDBJson(...)
},
"update": {
"expression": "SET #data = :data",
"expressionNames": {
"#data": "data"
},
"expressionValues": {
":data":
$util.dynamodb.toDynamoDBJson(...)
}
}
}
#if( $foreach.hasNext ),#end
#end
]
}
#else
#return
#end
createChildren-response.vtl
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($ctx.prev.result)
after.vtl
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($ctx.prev.result)
I figured it out. For the expected behaviour, one needs the 'after' mapper to return the necessary JSON to populate the overall mutation response. In my example above, after.vtl
needs to return a parent
and nothing else matters (in particular, the result of the individual function response mappers).
I ended up putting the output of the 'create parent' operation into ctx.stash
then returning ctx.stash
in after.vtl
, setting the other resolvers to {}
.
Note that, if your response has subtypes (with their own resolvers) and you return it sparse, AppSync will call the resolver. In the context of my example, it's enough to return the parent
without any children
and then the normal query resolver for "get children of a parent" will execute to populate the final response.