DEV Community

Cover image for AWS AppSync ToDo APIs with AWS Lambda Resolvers using Serverless Framework

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

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

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

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

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

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

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

Discussion (0)