This quick tutorial will demonstrate the basics of defining an AppSync Lambda Resolver with Cloudformation. We will use Python to write our Lambda.
Typically AppSync resolvers are defined against their specific data sources – Dynamodb or RDS being the most common – using the GraphQL velocity template language. For the most part – this works just fine – great in fact. If it is possible to resolve all the GraphQL operations for an application simply using CRUD operations using velocity template language – that is the way to go – reduces code bloat and your entire logic can be easily reviewed using the series of schema operations that AppSync is very good at implementing.
And especially against Dynamodb data sources – this is probably going to be the most performant implementation.
Lambda resolvers however are amongst the most powerful features in the already powerful AppSync toolkit. All too often, simply sticking to velocity template language – even with AppSyncs utility classes – we will find ourselves restricted. There are times when more complex business logic and operations simply cannot be done with velocity templates – think something like using encryption/decryption – calling custom libraries – triggering other services like SNS/SQS/Eventbridge – to name a few.
And even if that is not the restriction – sometimes it is just more convenient to use “regular” code.
No matter the reason – Lambda resolvers are a powerful way to extend your AppSync application’s functionality easily.
Lambda Resolvers explained – briefly
Lambda resolvers are no different from any other regular AppSync resolver from a GraphQL schema point of view.
In a typical schema with a selection of queries and mutations – the normal practice is to define a “resolver” for each operation. AppSync knows to invoke the particular resolver when a query or mutation operation is called from the client. And for Dynamodb/RDS resolvers, a velocity template language script is used to do a particular operation.
The prototypical example – a select operation from the client side is used to invoke a resolver where a select query is called against the datasource.
Lambda resolvers follow the same workflow – but instead of defining your Lambda in a velocity template script – you simply point your resolver to the Lambda definition and AppSync will invoke the Lambda.
You can use request mapping and response mapping templates with Lambda resolvers just like regular resolvers and the entire event is available in the Lambda context.
Lets demonstrate how this is done using a Python Lambda.
Before we begin in case you need it this post covers how to create a Python Lambda with Cloudformation.
Defining a sample AppSync schema
We will implement a Lambda resolver for this sample schema.
There is a getData query which expects a single “data” string response back.
AppSyncApi:
Type: "AWS::AppSync::GraphQLApi"
Properties:
Name: "sample-appsync-api"
AuthenticationType: "API_KEY"
AppSyncGraphQLSchema:
Type: "AWS::AppSync::GraphQLSchema"
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
Definition: |
schema {
query: Query
}
type Data {
data: String!
}
type Query {
getData(
key: String!
): Data!
}
Defining the Lambda Resolver in Cloudformation
To define a Lambda resolver – we will need to define an AppSync DataSource which will point to our Lambda function.
While we can define a request/response mapping template to perform pre/post operations for the resolver – we do not need to – for this example we will skip it – but keep in mind it is possible to do so.
DataReturningLambda:
Type: AWS::Serverless::Function
Properties:
Architectures:
- arm64
Runtime: python3.9
CodeUri: ./data-lambda/
Handler: data-lambda.event_handler
Description: Python Lambda Resolver to return the data string
FunctionName: data-lambda
AppSyncLambdaDataSource:
Type: "AWS::AppSync::DataSource"
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
Name: "AppSyncLambdaDataSource"
Description: "AppSync LambdaData Source referring to the DataReturningLambda"
Type: "AWS_LAMBDA"
LambdaConfig:
LambdaFunctionArn: !GetAtt DataReturningLambda.Arn
GetDataResolver:
Type: "AWS::AppSync::Resolver"
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: "Query"
FieldName: "getData"
DataSourceName: !GetAtt AppSyncLambdaDataSource.Name
That’s almost it- all we need to do now is code our Lambda.
We do need to grant our API permissions to invoke the Lambda.
Granting permissions to AppSync to invoke the Lambda
AppSyncApiLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt DataReturningLambda.Arn
Action: lambda:InvokeFunction
Principal: appsync.amazonaws.com
SourceAccount: !Ref 'AWS::AccountId'
SourceArn: !GetAtt AppSyncApi.Arn
Coding our Python Lambda Resolver
This is of course a very very simple Lambda – but the purpose is to demonstrate the setup for a more complex operation
#data-lambda.py
def event_handler(event, context):
key = event["key"]
return f"you sent {key}"
Concluding
To see a more complex application using AppSync Lambda resolvers – do check out my post on creating a serverless texting system with AppSync.