AWS made a serverless option of AWS RDS Aurora generally available, It would mean a breakthrough in building fully serverless architectures with relational database. No more managing connection pools with SQL, No more worrying about RDS instances, Cost is based on the compute time services are consumed, and there is no charge when code is not running.
In this post , my goal was to build a fully-functional AWS AppSync-driven backend and React frontend of a post management app. The app will deliver end users a set of features that allow public users view posts, admin users add/delete post, Let’s start!
Application Architecture
The application architecture uses:
- Serverless framework
- AWS Appsync
- AWS Cognito Userpool and Identity Pool
- AWS Aurora Serverless
- AWS Lambda
Prerequisites
The following must be done before following this guide:
-
Setup an AWS account
-
Install the AWS CLI
-
Configure the AWS CLI with user credentials
-
Install the Serverless framework from NPM
$ npm install -g serverless
Backend
Create a new service
Create a new service using the AWS Python template, specifying a unique name and an optional path.
$ sls create --template aws-nodejs --path simpleCMS
In the app folder, there is a serverless.yml file. This file is needed to configure how our application will behave.
Configuring Appsync
Install serverless-appsync-plugin
npm install serverless-appsync-plugin
Add serverless-appsync-plugin to the plugins section of serverless.yml
plugins:
- serverless-appsync-plugin
Add the following config to the custom section of serverless.yml
custom:
appSync:
region: ${self:provider.region}
name: ${self:service.name}-${self:provider.stage}
authenticationType: AWS_IAM
serviceRole: "${self:service.name}-AppSyncServiceRole"
userPoolConfig:
awsRegion: "${self:provider.region}"
userPoolId: ${self:custom.userPool.${self:provider.stage}}
defaultAction: ALLOW
mappingTemplates:
- type: Query
dataSource: LambdaSource
field: getPosts
request: "Query-getPosts-request.vtl"
response: "Query-getPosts-response.vtl"
- type: Post
dataSource: LambdaSource
field: author
request: "Query-author-request.vtl"
response: "Query-author-response.vtl"
- type: Mutation
field: addPost
request: Mutation-addPost-request.vtl
response: common-response.vtl
kind: PIPELINE
functions:
- addPost
- sendNotification
- type: Mutation
dataSource: LambdaSource
field: deletePost
request: Mutation-deletePost-request.vtl
response: common-response.vtl
functionConfigurations:
- dataSource: LambdaSource
name: "addPost"
request: "Function-addPost-request.vtl"
response: "common-response.vtl"
- dataSource: LambdaSource
name: "sendNotification"
request: "Function-sendNotification-request.vtl"
response: "Function-sendNotification-response.vtl"
dataSources:
- type: AWS_LAMBDA
name: LambdaSource
description: "Lambda DataSource"
config:
functionName: graphql
lambdaFunctionArn: { Fn::GetAtt: [GraphqlLambdaFunction, Arn] }
serviceRoleArn: { Fn::GetAtt: [AppSyncLambdaServiceRole, Arn] }
You can get all mapping templates from GitHub repo.
Next, create a IAM service role for Appsync:
<a href="https://medium.com/media/c0c0586f44892991396b65c048873c69/href">https://medium.com/media/c0c0586f44892991396b65c048873c69/href</a>
Configuring Cognito
Add following config to resources section in serverless.yml
<a href="https://medium.com/media/202cba04079fc980dbef56a9acb045f0/href">https://medium.com/media/202cba04079fc980dbef56a9acb045f0/href</a>
Note that above config would create a Cognito identity pool supports both authenticated and unauthenticated users, then a system admin user will be added to Cognito user pool and send out an email with temporary password to provided email address, eg.
InitialSystemAdminUser:
Type: AWS::Cognito::UserPoolUser
Properties:
UserPoolId:
Ref: CognitoUserPool
Username: "test@neami.app"
DesiredDeliveryMediums:
- EMAIL
ForceAliasCreation: true
UserAttributes:
- Name: email
Value: "test@neami.app"
- Name: name
Value: Admin
Configuring RDS Aurora Serverless
To setup RDS Aurora Serverless, we also need to create a VPC and at least 2 subnets to database, config should like as below
<a href="https://medium.com/media/a00a761b8968bb7d58a1e68462e8f823/href">https://medium.com/media/a00a761b8968bb7d58a1e68462e8f823/href</a>
To link Appsync lambda datasource to RDS, you will need to add Aurora RDS’ VPC config we just configured to providers section
provider:
vpc:
securityGroupIds:
- Fn::GetAtt: [DatabaseVpcSecurityGroup, GroupId]
subnetIds:
- Ref: PrivateSubnet1
- Ref: PrivateSubnet2
You can find full serverless.yml here.
Designing Graphql Schema
Schema files are text files, usually named schema.graphql. In this project schema would look like the following
<a href="https://medium.com/media/7f9316be3c4eba08176931df6b3661c2/href">https://medium.com/media/7f9316be3c4eba08176931df6b3661c2/href</a>
Adding Lambda function
Now, let’s update our handler.js to configure our database connection and do some query, This is how the handler.py file looks like:
<a href="https://medium.com/media/bf1dff7136bafac74e635e1ced2a8bfb/href">https://medium.com/media/bf1dff7136bafac74e635e1ced2a8bfb/href</a>
That is all about the backend, Let’s deploy the service.
Serverless framework makes deployments quick and painless. All you need to do is to run one command:
$ sls deploy --stage dev
It will automagically provision resources on AWS, package up and push all code to S3 from where it will be sent to the Lambdas. The terminal should show output similar to this
React Frontend
I assume you already have something loading just using react (create using create-react-app). To execute GraphQL queries from React with AWS AppSync, the following libraries are required:
- aws-amplify, aws-amplify-react - For Auth and other AWS calls
- aws-appsync, aws-appsync-react - Wrapper around apollo client which manages communication to graphql api endpoint
- react-apollo - Apollo client side library for React framework
- Other apollo libraries like apollo-link-http, apollo-link-state and apollo-cache-inmemory
I hope you have found this article useful, You can find complete project in my GitHub repo:
Learn more
- How to add a cache layer to Appsync API
- Building a Serverless Data Pipeline with AWS S3 Lamba and DynamoDB
- End to End Testing React apps with Selenium Python WebDriver Chrome PyTest and CircleCI
- How to build a React chat app with AWS API Gateway WebSockets, Custom Lambda Authorizer
- Running Selenium and Headless Chrome on AWS Lambda Layers