Amazon recently announced CloudFormation support for AppSync and all its features. Together with the Serverless Application Model it’s now dead simple to deploy a GraphQL API and custom Lambda resolvers without using the API at all. The GraphQL RSS Proxy example project is a serverless GraphQL API using AppSync, with an AWS Lambda function as a custom Query Resolver writting in Go.
Update: If you enjoy GraphQL and AppSync, you should see the updated guide and example project about using appsync-resolvers
, CloudFormation and SAM. The setup can be cleaned up a little bit more and uses a generic resolver handling.
Update: Read how you can add a custom domain for your GraphQL API with AppSync, CloudFront, Route53, and CloudFormation.
Introduction
Some weeks ago, I published an example project to deploy a GraphQL API using AWS AppSync. Back in the days, Amazon did not support AppSync in CloudFormation, this luckily has changed now. I was able to remove large parts of the Makefile
and the flow to deploy the projects is now even more simple.
Dependencies
To use the Serverless Application Model, you will need the awscli
. On a macOS system, just install it with homebrew:
$ > brew install awscli
Install Go packages
To build the Go
function, you will need two dependencies. Use dep
to install them.
# make install
$ > dep ensure
Configure Environment
For using the awscli
, you must have correct environment settings with an access key and secret.
Deploy
Thanks to the Serverless Application Model, deploying your AWS Lambda function and all CloudFormation resources is dead simple these days:
# Create S3 Bucket
$ > make configure
# Build the binary for Lambda
$ > make build-lambda
# Upload data to S3 Bucket
$ > make package
# Deploy CloudFormation Stack
$ > make deploy
# Get GraphQL information
$ > make outputs
[
{
"OutputKey": "APIKey",
"OutputValue": "da2-nuyzhanm5bcptga6yilkj7zluy",
"Description": "API Key"
},
{
"OutputKey": "GraphQL",
"OutputValue": "https://5qemu9oyli88sho8tl5rh0ejpil.appsync-api.eu-west-1.amazonaws.com/graphql",
"Description": "GraphQL URL"
}
]
Use the GraphQL API
You can use curl
to test your API or use a GraphQL UI. Just make sure to configure the needed authentication header with your API Key and use the correct URL:
$ > curl \
-XPOST https://5qemu9oyli88sho8tl5rh0ejpil.appsync-api.us-east-1.amazonaws.com/graphql \
-H "Content-Type:application/graphql" \
-H "x-api-key:da2-nuyzhanm5bcptga6yilkj7zluy" \
-d '{ "query": "query { feed { title url } }" }' | jq
{
"data": {
"feed": [
{
"title": "Deploy Golang Lambda with AWS Serverless Application Model",
"url": "https://sbstjn.com/golang-lambda-with-aws-sam-serverless-application-model.html"
},
{
"title": "68% Mechanical Keyboard with 68Keys.io",
"url": "https://sbstjn.com/build-your-own-mechanical-keyboard.html"
}
[...]
}
}
That’s awesome, right? No third-party frameworks, no large amount of dependencies or needs to bootstrap the project. Pure vendor tools, of course vendor-locked, but who cares?
AppSync with CloudFormation
And the best part, everything is configured using CloudFormation in the template.yml
resource file:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Resources:
Lambda:
Type: AWS::Serverless::Function
Properties:
Handler: dist/handler_linux
Runtime: go1.x
Tracing: Active
Environment:
Variables:
URL: https://sbstjn.com/feed.xml
Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: appsync.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: allow-access-to-lambda-from-appsync
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: lambda:invokeFunction
Resource:
- !GetAtt [Lambda, Arn]
- !Join ["", [!GetAtt [Lambda, Arn], ":*"]]
AppSyncAPI:
Type: AWS::AppSync::GraphQLApi
Properties:
Name: !Join [-, [!Ref ParamProjectName, !Ref ParamENV]]
AuthenticationType: API_KEY
AppSyncSchema:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId: !GetAtt [AppSyncAPI, ApiId]
Definition: !Ref ParamSchema
AppSyncDataSource:
Type: AWS::AppSync::DataSource
Properties:
ApiId: !GetAtt [AppSyncAPI, ApiId]
Name: source
Type: AWS_LAMBDA
LambdaConfig:
LambdaFunctionArn: !GetAtt [Lambda, Arn]
ServiceRoleArn: !GetAtt [Role, Arn]
AppSyncResolver:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt [AppSyncAPI, ApiId]
TypeName: Query
FieldName: feed
DataSourceName: !GetAtt [AppSyncDataSource, Name]
RequestMappingTemplate: '{ "version" : "2017-02-28", "operation": "Invoke", "payload": $util.toJson($context.arguments) }'
ResponseMappingTemplate: "$util.toJson($context.result)"
AppSyncAPIKey:
Type: AWS::AppSync::ApiKey
Properties:
ApiId: !GetAtt [AppSyncAPI, ApiId]
Expires: !Ref ParamKeyExpiration
Parameters:
ParamProjectName:
Type: String
ParamSchema:
Type: String
ParamENV:
Type: String
ParamKeyExpiration:
Type: Number
Outputs:
APIKey:
Description: API Key
Value: !GetAtt [AppSyncAPIKey, ApiKey]
GraphQL:
Description: GraphQL URL
Value: !GetAtt [AppSyncAPI, GraphQLUrl]
What’s your setup to deploy and maintain a GraphQL API using AWS AppSync? Let me know on Twitter what you this about this approach and if you know of any way to already do this with CloudFormation!