AWS AppSync ToDo APIs with AWS Lambda Resolvers using Serverless Framework

jaymitb

Jaymitkumar Bhoraniya

Posted on September 26, 2021

AWS AppSync ToDo APIs with AWS Lambda Resolvers using Serverless Framework

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


Enter fullscreen mode Exit fullscreen mode

2. Create basic NodeJs type template project using Serverless Framework



serverless create --template aws-nodejs


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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
),
};
};

Enter fullscreen mode Exit fullscreen mode



  1. 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                     
Enter fullscreen mode Exit fullscreen mode

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}

Enter fullscreen mode Exit fullscreen mode



  1. 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!
}

Enter fullscreen mode Exit fullscreen mode



  1. 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)
    })
Enter fullscreen mode Exit fullscreen mode

};

Enter fullscreen mode Exit fullscreen mode



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)
    })
Enter fullscreen mode Exit fullscreen mode

};

Enter fullscreen mode Exit fullscreen mode



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"
Enter fullscreen mode Exit fullscreen mode

}

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)
})
};

Enter fullscreen mode Exit fullscreen mode



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)
    })
Enter fullscreen mode Exit fullscreen mode

};

Enter fullscreen mode Exit fullscreen mode



  1. 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

Enter fullscreen mode Exit fullscreen mode



  1. Deploy using Serverless Framework




serverless deploy

Enter fullscreen mode Exit fullscreen mode


> 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

Enter fullscreen mode Exit fullscreen mode



  1. AWS AppSync APIs Verification Screenshots

AppSync API Verification - getTodos

getTodos API verification

AppSync API Verification - createTodo

createTodo API Verification

AppSync API Verification - deleteTodo

deleteTodo API Verification

11. Remove already provisioned resources



serverless remove

Enter fullscreen mode Exit fullscreen mode




Relevant Resources Link

💖 💪 🙅 🚩
jaymitb
Jaymitkumar Bhoraniya

Posted on September 26, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related