Query records for a list of keys in GraphQL…
If you are using AppSync GraphQL you must be familiar with the queries with filters. In fact, when you are creating a new type structure, AppSync provides default filters to scan an object in your data source. But how can you query records for a list of keys in GraphQL? Unfortunately, it is not defined in the default filters.
My basic requirement is like this
For example, the filter which is inserted when you are creating the data source will look like following
input EventFiltertInput { eventId: TableStringFilterInput eventName: TableStringFilterInput description: TableStringFilterInput } input TableStringFilterInput { ne: String eq: String le: String lt: String ge: String gt: String contains: String notContains: String between: [String] beginsWith: String }
This doesn’t provide a way to fetch data for a list of eventIds. So is there a way to do this?
Query records for a list of keys in GraphQL
In order to do this, the resource that you can find in AWS are:
and
In the second link you will find these ComparisonOperator in DynamoDB expressions. It states that the operators that you can use as follows
EQ | NE | LE | LT | GE | GT | NOT_NULL | NULL | CONTAINS | NOT_CONTAINS | BEGINS_WITH | IN | BETWEEN
In the latter link it explains the use of ComparisonOperator as
a
IN (
b
,
c
,
d
)
— true if a
is equal to any value in the list — for example, any of b
, c
or d
. The list can contain up to 100 values, separated by commas.
So it seems like the one that I can use!
But how can I use it in AppSync GraphQL?
How to use the IN ComparisonOperator with GraphQL Expressions
After going through the documentations, the best way to implement was using VTL (Velocity Template Language: http://velocity.apache.org/engine/2.0/vtl-reference.html) which is used in resolvers. Its a very simple but powerful language that we can use to do the processing in AppSync Resolvers.
My query in GraphQL Schema looks like the following:
listEventsForEventId(eventIds: [String], limit: Int, nextToken: String): EventConnection
So the filter expression I should have something like this:
"filter": { "expression" : "#eventId IN (:id_1, :id_2, :id_3)", "expressionNames" : { "#eventId" : "eventId" }, "expressionValues" : $utils.toJson(["event_id_1", "event_id_2", "event_id_3"]) }
This will work for a fixed number of parameters, but I need it to be dynamic for the length of the array. For this, VTL would come in handy. So first go to the AppSync and select the resolver you want to add by clicking on Attach button.
Following was my resolver:
{ "version": "2017-02-28", "operation": "Scan", "limit": $util.defaultIfNull($ctx.args.limit, 20), #set( $expNames = {} ) #set( $expValues = {} ) #set( $expression = "#eventId IN (" ) #set( $index = 0 ) #foreach( $entry in $context.arguments.eventIds ) #set( $index = $index +1 ) #if( $entry) #if ($context.arguments.eventIds.size() == $index) #set( $expression = "${expression} :eventId${index})" ) #else #set( $expression = "${expression} :eventId${index}, " ) #end $!{expValues.put(":eventId${index}", { "S" : "${entry}" })} #end #end "filter": { "expression" : "${expression}", "expressionNames" : { "#eventId" : "eventId" }, "expressionValues" : $utils.toJson($expValues) }, "nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null)), }
And response mapping template
$util.toJson($ctx.result)
So in the resolver I first declared variables with VTL
#set( $expNames = {} ) #set( $expValues = {} ) #set( $expression = "#eventId IN (" ) #set( $index = 0 )
This will hold the values for my expression, expressionNames and expressionValues of the filter.
Next, I will iterate through the resolver parameters to construct the filter expression I needed to execute IN ComparisonOperator
#foreach( $entry in $context.arguments.eventIds ) #set( $index = $index +1 ) #if( $entry) #if ($context.arguments.eventIds.size() == $index) #set( $expression = "${expression} :eventId${index})" ) #else #set( $expression = "${expression} :eventId${index}, " ) #end $!{expValues.put(":eventId${index}", { "S" : "${entry}" })} #end #end
This will set the values I needed for the filter.
Finally add it to the resolver.
"filter": { "expression" : "${expression}", "expressionNames" : { "#eventId" : "eventId" }, "expressionValues" : $utils.toJson($expValues) }, "nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null)),
Thats it! you will be able to pass an array of strings for the endpoint and it will give you all the events precisely! Execute a simple query in AppSync to test it.