AWS AppSync ToDo APIs with AWS Lambda Resolvers using Serverless Framework
Jaymitkumar Bhoraniya
Posted on September 26, 2021
Implement basic ToDo APIs using AWS AppSync with AWS Lambda Resolvers by using Serverless Framework
Application Stack:
- Serverless Framework
- AWS AppSync
- AWS Lambda
- AWS DynamoDB
- AWS IAM
- NodeJs
- GraphQL
Install NodeJs as per instruction given here
1. Install Serverless Framework
npm install -g serverless
2. Create basic NodeJs type template project using Serverless Framework
serverless create --template aws-nodejs
By default it will generate a folder structure like this:
- handler.js
- serverless.yml
Default serverless.yml will have below IaC code:
service: default-aws-nodejs
frameworkVersion: '2'
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: 20201221
functions:
hello:
handler: handler.hello
Default handler.js will have below NodeJs code:
'use strict';
module.exports.hello = async (event) => {
return {
statusCode: 200,
body: JSON.stringify(
{
message: 'Go Serverless v1.0! Your function executed successfully!',
input: event,
},
null,
2
),
};
};
- Install required Serverless Framework Plugins
- npm install serverless-appsync-plugin
- npm install serverless-pseudo-parameters
4. Modify serverless.yml Infrastructure as code to provision needful AppSync, DynamoDb, Lambda resources, and relevant configuration
service: appsync-todo-apis-demo
frameworkVersion: "2"
plugins:
- serverless-appsync-plugin
- serverless-pseudo-parameters
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: ap-south-1
lambdaHashingVersion: 20201221
iam:
role:
statements:
- Effect: "Allow"
Action:
- "dynamodb:PutItem"
- "dynamodb:UpdateItem"
- "dynamodb:DeleteItem"
- "dynamodb:GetItem"
- "dynamodb:Scan"
Resource: "arn:aws:dynamodb:#{AWS::Region}:*:table/${self:custom.TODO_TABLE}"
custom:
TODO_TABLE: todo-table
appSync:
name: appsync-todo-apis-demo
authenticationType: API_KEY
mappingTemplates:
- type: Mutation
field: createTodo
dataSource: createTodoFunction
request: false
response: false
- type: Mutation
field: updateTodo
dataSource: updateTodoFunction
request: false
response: false
- type: Mutation
field: deleteTodo
dataSource: deleteTodoFunction
request: false
response: false
- type: Query
field: getTodos
dataSource: getTodosFunction
request: false
response: false
dataSources:
- type: AWS_LAMBDA
name: createTodoFunction
config:
functionName: createTodo
- type: AWS_LAMBDA
name: updateTodoFunction
config:
functionName: updateTodo
- type: AWS_LAMBDA
name: deleteTodoFunction
config:
functionName: deleteTodo
- type: AWS_LAMBDA
name: getTodosFunction
config:
functionName: getTodos
functions:
getTodos:
handler: functions/getTodos.handler
environment:
TODO_TABLE_NAME: !Ref TodoTable
createTodo:
handler: functions/createTodo.handler
environment:
TODO_TABLE_NAME: !Ref TodoTable
updateTodo:
handler: functions/updateTodo.handler
environment:
TODO_TABLE_NAME: !Ref TodoTable
deleteTodo:
handler: functions/deleteTodo.handler
environment:
TODO_TABLE_NAME: !Ref TodoTable
resources:
Resources:
TodoTable:
Type: "AWS::DynamoDB::Table"
Properties:
KeySchema:
- AttributeName: id
KeyType: HASH
AttributeDefinitions:
- AttributeName: id
AttributeType: S
BillingMode: PAY_PER_REQUEST
TableName: ${self:custom.TODO_TABLE}
- Create GraphQL Schema new file schema.graphql
schema {
query: Query
mutation: Mutation
}
type Query {
getTodos: [Get!]!
}
type Mutation {
createTodo(id: ID!, name: String!, description: String!): Save
updateTodo(id: ID!, name: String!, description: String!): Update
deleteTodo(id: ID!): Delete
}
type Get {
id: ID!
name: String
description: String
}
type Save {
id: ID!
name: String
description: String
}
type Update {
id: ID!
name: String
description: String
}
type Delete {
id: ID!
}
- Implement AWS Lambda Resolver CRUD handlers
Create new folder 'functions' with this files:
- createTodo.js
- deleteTodo.js
- getTodos.js
- updateTodo.js
6.1 createTodo.js handler
const AWS = require('aws-sdk')
const dynamodb = new AWS.DynamoDB()
module.exports.handler = async (event) => {
const id = event.arguments.id
const name = event.arguments.name
const description = event.arguments.description
const params = {
Item: {
"id": {
S: id
},
"name": {
S: name
},
"description": {
S: description
}
},
ReturnConsumedCapacity: "TOTAL",
TableName: process.env.TODO_TABLE_NAME
}
return dynamodb.putItem(params).promise()
.then(data => {
return {
id,
name,
description
}
})
.catch(err => {
console.log(err)
})
};
6.2 deleteTodo.js handler
const AWS = require('aws-sdk')
const dynamodb = new AWS.DynamoDB()
module.exports.handler = async (event) => {
const id = event.arguments.id
const params = {
Key: {
"id": {
S: id
}
},
TableName: process.env.TODO_TABLE_NAME
}
return dynamodb.deleteItem(params).promise()
.then(data => {
return {
id
}
})
.catch(err => {
console.log(err)
})
};
6.3 updateTodo.js handler
const AWS = require('aws-sdk')
const dynamodb = new AWS.DynamoDB()
module.exports.handler = async (event) => {
const id = event.arguments.id
const name = event.arguments.name
const description = event.arguments.description
const params = {
ExpressionAttributeNames: {
"#n": "name",
"#d": "description"
},
ExpressionAttributeValues: {
":n": {
S: name
},
":d": {
S: description
}
},
Key: {
"id": {
S: id
}
},
ReturnValues: "ALL_NEW",
TableName: process.env.TODO_TABLE_NAME,
UpdateExpression: "SET #n = :n, #d = :d"
}
return dynamodb.updateItem(params).promise()
.then(data => {
const body = data.Attributes
return {
id: body.id.S,
name: body.name.S,
description: body.description.S
}
})
.catch(err => {
console.log(err)
})
};
6.4 getTodos.js handler
const AWS = require('aws-sdk')
const dynamodb = new AWS.DynamoDB()
module.exports.handler = async (event) => {
const params = {
TableName: process.env.TODO_TABLE_NAME
}
return dynamodb.scan(params).promise()
.then(data => {
const todoList = [];
for (let i = 0; i < data.Items.length; i++) {
todoList.push({
id: data.Items[i].id.S,
name: data.Items[i].name.S,
description: data.Items[i].description.S
});
}
return todoList;
})
.catch(err => {
console.log(err)
})
};
- Create AWS IAM user with programmatic access and give needful permission that allows Serverless Framework to provision resources
The Serverless Framework needs access to your cloud provider account so that it can create and manage resources on your behalf.
During Creating AWS IAM user copy API Key & Secret.
8. Configure AWS Credentials for Serverless Framework Deployment
serverless config credentials --provider aws --key xxxxx --secret xxxxx
- Deploy using Serverless Framework
serverless deploy
> serverless deploy
AppSync Plugin: GraphQl schema valid
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service appsync-todo-apis-demo.zip file to S3 (16.98 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..........................................................................................
Serverless: Stack update finished...
Service Information
service: appsync-todo-apis-demo
stage: dev
region: ap-south-1
stack: appsync-todo-apis-demo-dev
resources: 31
api keys:
None
appsync api keys:
xxxxxxxxxxxxxxx
endpoints:
None
appsync endpoints:
https://xxxxxxxxxxxxxxx.appsync-api.ap-south-1.amazonaws.com/graphql
functions:
getTodos: appsync-todo-apis-demo-dev-getTodos
createTodo: appsync-todo-apis-demo-dev-createTodo
updateTodo: appsync-todo-apis-demo-dev-updateTodo
deleteTodo: appsync-todo-apis-demo-dev-deleteTodo
layers:
None
- AWS AppSync APIs Verification Screenshots
AppSync API Verification - getTodos
AppSync API Verification - createTodo
AppSync API Verification - deleteTodo
11. Remove already provisioned resources
serverless remove
Relevant Resources Link
- Serverless Framework: https://www.serverless.com/
- AWS AppSync: https://aws.amazon.com/appsync/
- AWS Lambda: https://aws.amazon.com/lambda/
- NodeJs: https://nodejs.org/
- GraphQL: https://graphql.org/
- AWS DynamoDB: https://aws.amazon.com/dynamodb/
- AWS IAM: https://aws.amazon.com/iam/
Posted on September 26, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.