Reputation: 1034
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?
Upvotes: 16
Views: 9580
Reputation: 539
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
}
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.
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.
Upvotes: 16
Reputation: 5508
You should be able to find consistentRead
in $context.info.variables
($context.info.variables.consistentRead
):
https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html#aws-appsync-resolver-context-reference-info
Upvotes: -1
Reputation: 55
Simply in the child use $ctx.source.id
where id
is the parameter you need reference from the parent.
Upvotes: -4
Reputation: 1
Add this to BookQuery Response Mapping Template
#set( $book = $ctx.result )
#set($Articles = []);
#foreach($article in $book.articles)
#set( $newArticle = $article )
$util.qr($newArticle.put("bookID", $book.id))
$util.qr($Articles.add($newArticle))
#end
$util.qr($book.put("articles", $Articles))
$util.toJson($book)
Now, every article will have bookID
Upvotes: 0
Reputation: 121
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.
Upvotes: 9
Reputation: 620
You don't need to pass arguments to sub-query. Base on your schema and use-case, I think you can adjust your schema like below to have a relationship between Author
and Book
type Author {
# parent's id
bookID: ID!
# author id
id: ID!
name: String!
}
type Book {
id: ID!
title: String!
author: [Author]!
}
type Mutation {
insertAuthor(bookID: ID!, id: ID!, name: String!): Author
insertBook(id: ID!, title: String!): Book
}
type Query {
getBook(id: ID!): Book
}
- Create table Author with Author.bookID
as a primary key and Author.id
as a sort key
- Create table Book with Book.id
as a primary key
Then, you have to attach a resolver for Book.author
And here is a resolver for insertAuthor
mutation
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
"bookID" : $util.dynamodb.toDynamoDBJson($ctx.args.bookID),
"id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
},
"attributeValues" : {
"name" : $util.dynamodb.toDynamoDBJson($ctx.args.name)
}
}
And when you do query getBook
you will get a list of author that has the same book id as below
Upvotes: -3