Creating APIs with NodeJS, DynamoDB and AWS Lambda: A better workflow with dynamoose
Tags: node, aws, dynamodb
Published: 2021-01-22 05:40:46

In this article, we'll create CRUD APIs with AWS Lambda and NodeJS and we'll be using 
 [Dynamoose](https://dynamoosejs.com/getting_started/Introduction) to get better understanding of our DB model and to have a better workflow since Dynamoose takes away the pain of writing CloudFormation code for the DynamoDB table and other hassles. 

> Prerequisite
- I assume you've AWS account
- You also should've node, npm and serverless installed. If you don't have serverless installed, you can install it through npm using ``` npm i -g serverless ```
- Since we'll be using Serverless Framework for deploying our stack, you've set-up the credentials with AWS. If not, Head towards the  [Serverless Documentation](https://www.serverless.com/framework/docs/providers/aws/guide/installation/) do the quick set-up.

Here's the file structure we'll be using:

![filestruct.JPG](https://cdn.hashnode.com/res/hashnode/image/upload/v1611287802611/t8Grxw3hT.jpeg)

> Getting Started

We need to generate a simple template serverless CLI. In order to do so:
```
sls create -t aws-nodejs
```
Now, Navigate into the directory and delete the *handler.js* file since we'll be creating our own handler.

> Folder structure set-up

- First create 3 new directories namely **functions**, **helper**, and **Model**
- Inside the the functions directory, create another directory namely users
- Inside the helper directory, create two files, *error.js* and *success.js*
- Inside Model directory, create a new file called *UserModel.js*

> APIs List

We'll be creating following 5 APIs related to users.
- createUser
- getAllUsers
- getUserById
- updateUser
- deleteUser

> Installing required packages:

We need some npm packages in order to get our code working. so run the following command
```
npm i dynamoose uuid
npm i aws-sdk -D
```

> Setting up CloudFormation code:

Open the Serverless.yml file and paste the following code:
```
service: dynamo-tut

provider:
  name: aws
  runtime: nodejs12.x
  region: us-east-1
  profile: personal #Replace it with your own profile name


  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
        - dynamodb:CreateTable
      Resource: '*'

functions:
  writeToDB:
    handler: functions/users/createUser.main
    events:
      - http:
          path: addUser
          method: post
          cors: true

  getAllUsers:
    handler: functions/users/getAllUsers.main
    events:
      - http:
          path: getAll
          method: get
          cors: true

  getUserById:
    handler: functions/users/getUserById.main
    events:
      - http:
          path: getOne
          method: get
          cors: true

  updateUser:
    handler: functions/users/updateUser.main
    events:
      - http:
          path: update
          method: put
          cors: true

  deleteUser:
    handler: functions/users/deleteUser.main
    events:
      - http:
          path: delete
          method: delete
          cors: true
```
We're doing just basic stuffs, setting up provider details, AWS IAM Permissions for our lamba functions and then defining the handler for our functions with API Gateway attached.

Now, paste the following code in *error.js* and *success.js* inside **helper** directory

```
// success.js
const getSuccessResponse = (info) => {
  return {
    statusCode: 200,
    headers: {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Credentials': true,
    },
    body: JSON.stringify({
      message: 'Request approved for performing operation',
      data: info,
      success: true,
    }),
  };
};

module.exports = { getSuccessResponse };

// error.js
const getErrorResponse = (info) => {
  console.log(info);
  return {
    statusCode: info.statusCode || 500,
    headers: {
      'Content-Type': 'text/plain',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Credentials': true,
    },
    body: JSON.stringify(info),
  };
};

module.exports = { getErrorResponse };
```
These functions ensures that our functions contains the response headers and cors policy so that our front-end will not have any issues. We'll use these helper functions to throw out responses.

Now, we need to define the Model which will be then used by Dynamoose to create DDB tables under the hood.

Create a file *Models/UserModel.js* and paste the following code:
```
const dynamoose = require('dynamoose');

const schema = new dynamoose.Schema(
  {
    id: {
      type: String,
      hashKey: true,
    },
    name: String,
    age: Number,
  },
  {
    timestamps: true,
  }
);

const UsersModel = dynamoose.model('userstable', schema, {
  create: true,
  throughput: {
    read: 5,
    write: 5,
  },
});
module.exports = { UsersModel };
```

Now, for the handler part, create 5 files inside **functions/users** and paste the following code:

```
// createUser.js

'use strict';

const { getSuccessResponse } = require('../../helper/success');
const { getErrorResponse } = require('../../helper/error');

const { v4: uuidv4 } = require('uuid');
const { UsersModel } = require('../../Models/UserModel');

module.exports.main = async (event) => {
  try {
    const request = JSON.parse(event.body);
    const { name, email } = request;

    const result = await UsersModel.create({
      id: uuidv4(),
      name,
      email,
    });

    return getSuccessResponse(result);
  } catch (error) {
    console.log(error);
    return getErrorResponse(error);
  }
};

// getAllUsers.js

'use strict';

const { getSuccessResponse } = require('../../helper/success');
const { getErrorResponse } = require('../../helper/error');

const { UsersModel } = require('../../Models/UserModel');

module.exports.main = async (event) => {
  try {
    const result = await UsersModel.scan().exec();
    return getSuccessResponse(result);
  } catch (error) {
    return getErrorResponse(error);
  }
};

// getUserById.js

'use strict';
const { getSuccessResponse } = require('../../helper/success');
const { getErrorResponse } = require('../../helper/error');

const { UsersModel } = require('../../Models/UserModel');

module.exports.main = async (event) => {
  try {
    const queryStringParameters = event.queryStringParameters;
    const { id } = queryStringParameters;

    const result = await UsersModel.get({ id });
    return getSuccessResponse(result);
  } catch (error) {
    return getErrorResponse(error);
  }
};

// updateUser.js

'use strict';

const { getSuccessResponse } = require('../../helper/success');
const { getErrorResponse } = require('../../helper/error');

const { UsersModel } = require('../../Models/UserModel');

module.exports.main = async (event) => {
  try {
    const request = JSON.parse(event.body);
    const { id, ...data } = request;

    const result = await UsersModel.update({ id }, { ...data });
    return getSuccessResponse(result);
  } catch (error) {
    return getErrorResponse(error);
  }
};

// deleteUser.js

'use strict';

const { getSuccessResponse } = require('../../helper/success');
const { getErrorResponse } = require('../../helper/error');

const { UsersModel } = require('../../Models/UserModel');

module.exports.main = async (event) => {
  try {
    const request = JSON.parse(event.body);
    const { id } = request;

    const result = await UsersModel.delete({ id });
    return getSuccessResponse(result);
  } catch (error) {
    return getErrorResponse(error);
  }
};

```

> Deployment

For deployment, the following command will deploy the stack on AWS and return the endpoints for all the functions:
``` sls deploy -v ```

> Testing

Once the deployment is complete, you'll find the endpoints in the output section of your terminal. Copy and paste those endpoints on Postman and hit with appropriate params and payload. If you've followed everything correctly, it will return you with results.

Here's the  [Repo](https://github.com/rajandmr/dynamodb-dynamoose-node)  with complete code written and tested.

> Subscribe

If you like reading what i write, consider subscribing to the newsletter so you won't miss any stories.