Use DynamoDB in NestJS Application With Serverless Framework on AWS

Muhd Mohaiminul Islam
The Startup
Published in
4 min readJan 16, 2021

In this article, we’re going to learn how we can use DynamoDB in the NestJS application using the Serverless framework on the AWS platform.

DynamoDB in NestJS application with Serverless framework on AWS

Before we proceed, first we need to set up a NestJS application into the Serverless framework.

Once done the setup of the NestJS application in the Serverless framework, we can proceed with our hands-on steps.

Hands-on Steps:

Install node dependencies:

This node package that we need to install first before we proceed.

npm install --save aws-sdk

Update configuration file:

Here we need to add all these additional configurations of DynamoDB and its permission for the Serverless application.

// serverless.yml...provider:
...
environment:
...
USERS_TABLE_NAME: ${self:custom.usersTable.name}
iamRoleStatements: ${file(iam/usersTableIAM.yml)}
resources:
Resources:
usersTable: ${file(resources/usersTable.yml):usersTable}
...custom:
usersTable:
name: ${self:provider.stage}-users
arn: !GetAtt usersTable.Arn

Define IAM roles for User Table:

These are the permissions of DynamoDB that need to present in Serverless configurations.

// iam/usersTableIAM.yml- Effect: 'Allow'
Action:
- 'dynamodb:Query'
- 'dynamodb:Scan'
- 'dynamodb:GetItem'
- 'dynamodb:PutItem'
- 'dynamodb:UpdateItem'
- 'dynamodb:DeleteItem'
Resource:
- ${self:custom.usersTable.arn}

Define Resources for User Table:

These are the definitions of the User table that need to present in Serverless configurations.

// resources/usersTable.ymlusersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.usersTable.name}
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST

Create a User module:

We need to run these commands to create a User module with its controller and service.

nest generate module user
nest generate controller user --no-spec
nest generate service user --no-spec

Define the controller for the User:

Here we need to define all the endpoints that the User module allows access to everyone.

// src/user/user.controller.tsimport {
Body,
Controller,
Get,
NotFoundException,
Param,
Post,
} from '@nestjs/common';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}

@Post()
async createUser(@Body() body: any): Promise<any> {
const user = await this.userService.createUser(body);

return { status: true, data: user };
}
@Get(':id')
async getUserById(@Param() { id }: any): Promise<any> {
const user = await this.userService.getUserById(id);
if (!user) {
throw new NotFoundException(`User with ID "${id}" not found`);
}
return { status: true, data: user };
}
}

Define the service for the User:

Here we need to define all the implementations that the User controller needs to take care of.

// src/user/user.service.tsimport { Injectable, InternalServerErrorException } from '@nestjs/common';
import * as AWS from 'aws-sdk';
import { v4 as uuid } from 'uuid';
const dynamoDB = new AWS.DynamoDB.DocumentClient();@Injectable()
export class UserService {
async createUser(dto: any): Promise<any> {
const user = {
id: uuid(),
...dto,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
try {
await dynamoDB
.put({
TableName: process.env.USERS_TABLE_NAME,
Item: user,
})
.promise();
} catch (error) {
throw new InternalServerErrorException(error);
}
return user;
}
async getUserById(id: string): Promise<any> {
let user;
try {
const result = await dynamoDB
.get({
TableName: process.env.USERS_TABLE_NAME,
Key: { id },
})
.promise();
user = result.Item;
} catch (error) {
throw new InternalServerErrorException(error);
}
return user;
}
}

Build and deploy the Serverless application:

Make sure, we always build our application before deploying to AWS. Because all the changes will reflect after the build process is successful as we upload the files from the dist directory into AWS S3.

npm run build && serverless deploy

AWS DynamoDB Local:

To speed up our development cycles, we can run and test our application on our local machine. And as we use DynamoDB, therefore, we can add this serverless-dynamodb-local plugin to run our Serverless application to run in the local machine.

Install node dependencies:

Install this npm package before we proceed.

npm install --save serverless-dynamodb-local

Update configuration file:

We need to add this serverless-dynamodb-local plugin in our Serverless configuration file with other changes.

// serverless.yml...plugins:
- serverless-dynamodb-local
...
provider:
...
environment:
...
DYNAMODB_ENDPOINT: ${self:custom.endpoints.dynamodbURL}
...
custom:
dynamodb:
stages:
- ${self:provider.stage}
start:
migrate: true
endpoints:
dynamodbURL: 'http://localhost:8000'
...
...

Update service file:

We need to modify the DocumentClient declaration for DynamoDB to check whether we use the local endpoint or not during the process.

// src/user/user.service.ts...let dynamoDB;
if (process.env.IS_OFFLINE === 'true') {
dynamoDB = new AWS.DynamoDB.DocumentClient({
region: 'localhost',
endpoint: process.env.DYNAMODB_ENDPOINT,
});
} else {
dynamoDB = new AWS.DynamoDB.DocumentClient();
}
...

Build and run the Serverless application:

Same as before, we always build our application before running the application. Because all the changes will reflect after the build process is successful as our build codes point to the dist directory.

serverless dynamodb installnpm run build && serverless offline start

--

--