Build CRUD API with AWS Lambda and DynamoDB

In this post we will be building simple CRUD API to manage tasks and in the upcoming posts we will use this project as a base to explore other AWS services.

But in order to follow along with this post you need to have a basic understanding of DynamoDB and how to setup your lambda function and DynamoDB database using the serverless framework.

So, let's begin

Project structure

The code is structured using a service-oriented approach where we basically follow a 3-layer architecture.

The main advantage of using the 3-layer-architecture is that it creates a level of abstraction and allows you to manage your code base more efficiently.

![service-oriented architecture] (dev-to-uploads.s3.amazonaws.com/uploads/art..)

So, create a folder called src and create 3 files:

  • functions.js - which basically is your controller
  • io.js - which will be your service layer
  • model.js - which will be your data access layer

Tips

Before we start just want to drop in few serverless commands that will speed up your deployment and debugging process.

  1. serverless deploy function --function functionName

  2. serverless logs -f functionName -t

The first command allows you to only deploy a single function rather than your entire infrastructure.

The second command allows you to view the logs for a given function.

Database operations

Now in the model.js file we will define all the operations we will be performing on the todo table.

In the code below I'm creating a class called Todo which will contain the respective methods fetchAll fetchById put deleteById.

You can follow the DynamoDB 101 post to understand how it works

const aws = require('aws-sdk');

class Todo {
  client = new aws.DynamoDB.DocumentClient();
  TableName = "todoTable"

  async put(Item) {
    const params = {
      TableName: this.TableName,
      Item
    }
    const result = await this.client.put(params).promise()

    return true
  }


  async fetchById(key) {
    const params = {
      TableName: this.TableName,
      Key: {
        userId: key
      }
    }
    const result = await this.client.get(params).promise()
    return result.Item
  }

  async fetchAll(key) {
    const params = {
      TableName: this.TableName,
    }
    const result = await this.client.scan(params).promise()
    return result.Items
  }

  async deleteById(key) {
    const params = {
      TableName: this.TableName,
      Key: {
        userId: key
      }
    }
    const result = await this.client.delete(params).promise()
    return true
  }
}

module.exports = new Todo();

Service layer

This layer will basically encapsulate your business logic since this is a basic todo app we'll just extract the body and rout parameter from the request and return it.

module.exports = {
  input: (event) => JSON.parse(event.body),
  params: (event) => event.pathParameters
}

Controller

This section of the project will contain all your function logic that we'll link in the serverless.yml file in a sec.

if you're not sure how to write your functions checkout the post on serverless functions for dummies

'use strict';
const io = require('./io')
const Todo = require('./model')

const getAllTasks = async (event) => {
  const data = await Todo.fetchAll();
  return data
};


const taskById = async (event) => {
  const { id } = io.params(event)
  const data = await Todo.fetchById(id);
  return data
}

const createTask = async (event) => {
  const body = io.input(event);
  const data = await Todo.put(body)
  return JSON.stringify({ message: "created task" })
}

const updateTask = async (event) => {
  const body = io.input(event);
  const data = await Todo.put(body)

  return JSON.stringify({ message: "updated task" })
}

const deleteTask = async (event) => {
  const { id } = io.params(event)
  const data = await Todo.deleteById(id);
  return JSON.stringify({ message: "deleted task with id = " + id })
}


module.exports = {
  getAllTasks,
  taskById,
  createTask,
  updateTask,
  deleteTask
}

Serverless config

Try to setup the serverless configs on your own, the below code is just for your reference.

'use strict';
const io = require('./io')
const Todo = require('./model')

const getAllTasks = async (event) => {
  const data = await Todo.fetchAll();
  return data
};


const taskById = async (event) => {
  const { id } = io.params(event)
  const data = await Todo.fetchById(id);
  return data
}

const createTask = async (event) => {
  const body = io.input(event);
  const data = await Todo.put(body)
  return JSON.stringify({ message: "created task" })
}

const updateTask = async (event) => {
  const body = io.input(event);
  const data = await Todo.put(body)

  return JSON.stringify({ message: "updated task" })
}

const deleteTask = async (event) => {
  const { id } = io.params(event)
  const data = await Todo.deleteById(id);
  return JSON.stringify({ message: "deleted task with id = " + id })
}


module.exports = {
  getAllTasks,
  taskById,
  createTask,
  updateTask,
  deleteTask
}

now run serverless deploy and try out your apis using postman.