Building and deploying web applications can become challenging when setting up, provisioning, maintaining, and scaling the underlying infrastructure. However, we can use the Serverless Framework to make things much easier.
In this tutorial, we’ll learn how to build a serverless blog application in NestJS using DynamoDB and the Serverless Framework. The code for this tutorial is available on GitHub. Let’s get started!
- What is NestJS?
- What is DynamoDB?
- What is the Serverless Framework?
- Getting started
- Install dependencies
- Configure the Serverless Framework
- Convert our application to AWS Lambda
- Create a blog service
- Create blog controllers
- Test the application
What is NestJS?
NestJS is a Node.js framework for building TypeScript-based server-side applications that are fast, tested, scalable, and loosely-linked. NestJS uses solid HTTP server frameworks like Express or Fastify, offering an overlay to abstract them and expose their APIs directly to developers. This gives developers the ability to leverage the plethora of third-party modules available for the underlying platform.
NestJS supports database management systems like PostgreSQL, MySQL, and, in this tutorial, YugabyteDB. NestJS also includes dependency injections out of the box.
What is DynamoDB?
As part of the Amazon Web Services portfolio, Amazon offers DynamoDB, a fully managed, proprietary NoSQL database service that supports key-value and document data formats. DynamoDB presents a data architecture comparable to Dynamo, taking its name from it, although DynamoDB has a distinct underlying implementation.
What is the Serverless Framework?
The Serverless Framework is an open source CLI that allows you to design, deploy, debug, and protect serverless apps with minimum complexity and expense while providing infrastructure resources from AWS, Azure, and Google.
The Serverless Framework offers out-of-the-box structure, automation, and best practices support, allowing you to focus on developing sophisticated, event-driven, serverless systems made up of functions and events.
Getting started
To start building and deploying AWS serverless functions, you need to set up an AWS account. Then, set up your credentials by running the command below:
aws configure
The setup above is required if you wish to deploy your functions to AWS. Next, install the Serverless Framework globally with the command below:
npm install -g serverless
Then, install the NestJS CLI:
npm i -g @nestjs/cli
Create a NestJS application with the command below:
nest new nest-serverless && cd nest-serverless
Install dependencies
Now, let’s install the required dependencies with the command below:
npm install aws-lambda aws-serverless-express express aws-sdk --save
At this point, we have the initial setup for our serverless application.
Configure the Serverless Framework
Now, let’s add some actual configurations for our serverless application. Create a serverless.yml
file in the project’s root directory with the code snippet below:
service: nest-serverless plugins: - serverless-plugin-optimize - serverless-offline - serverless-dynamodb-local functions: app: handler: dist/main.handler events: - http: method: any path: /{any+} provider: name: aws runtime: nodejs14.x apiGateway: shouldStartNameWithService: true environment: AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1 NODE_OPTIONS: --enable-source-maps --stack-trace-limit=1000 iam: role: statements: - Effect: 'Allow' Action: - 'dynamodb:DescribeTable' - 'dynamodb:Query' - 'dynamodb:Scan' - 'dynamodb:GetItem' - 'dynamodb:PutItem' - 'dynamodb:UpdateItem' - 'dynamodb:DeleteItem' Resource: arn:aws:dynamodb:us-west-2:*:table/BlogsTable custom: esbuild: bundle: true minify: false sourcemap: true exclude: aws-sdk target: node14 define: 'require.resolve: undefined' platform: node concurrency: 10 dynamodb: start: port: 5000 inMemory: true migrate: true stages: dev resources: Resources: TodosTable: Type: AWS::DynamoDB::Table Properties: TableName: BlogsTable AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1
In our serveless.yml
file, we specified the version of the Serverless Framework that our project is running on in FrameworkVersion
. In our case, we use version 3, which is the latest release at the time of writing.
We specify which plugins are required to replace or enhance the functionality of our project in Plugins
. We have two plugins, serverless-esbuild and Serverless Offline, which allow us to execute our code locally. DynamoDB can be operated locally with serverless-dynamodb-local.
In Provider
, we configure the cloud provider for our project. To provide our Lambda functions with reading and writing access to our DynamoDB resource table, we set various cloud provider characteristics, like the name
, runtime
, apiGateway
, and iam
statements
In Resources
, we populate our DynamoDB database using our cloudFormation
resource templates. In this section, we declare specific characteristics like the tableName
, AttributeDefinitions
, where we specify our table’s primary key ID, and ProvisionedThroughput
, where we describe the number of units our database can read and write in one second.
We create our own configuration in Custom
. For our DynamoDB database, we set port 5000
. Finally, in Functions
, we configure our Lambda functions, routes, and route handlers.
To enable us to run our application locally on our computer, we need to install the following plugins:
serverless -dynamodb-local
: Connects to DynamoDB locally on our computerserverless-offline
: Starts the application locallyserverless-plugin-optimize
: Enables us to run the application locally
Run the command below to install the plugins above:
serverless plugin install -n serverless-plugin-optimize serverless plugin install -n serverless-dynamodb-local serverless plugin install -n serverless-offline
Finally, let’s install DynamoDB locally with the command below:
serverless dynamodb install
Convert our application to AWS Lambda
We’ll convert our NestJS application to an AWS Lambda function in our src/main.ts
file. First, we need to map our application to an Expess app with the code snippet below:
import { NestFactory } from '@nestjs/core'; import { ExpressAdapter } from '@nestjs/platform-express'; import { INestApplication } from '@nestjs/common'; import { AppModule } from './app.module'; import { Express } from 'express'; import { Server } from 'http'; import { Context } from 'aws-lambda'; import { createServer, proxy, Response } from 'aws-serverless-express'; import * as express from 'express'; let cachedServer: Server; async function createExpressApp( expressApp: Express, ): Promise<INestApplication> { const app = await NestFactory.create( AppModule, new ExpressAdapter(expressApp), ); return app; } ...
Then, we’ll convert our Express application into an AWS Lambda function so that we can run our applications as Lambda functions:
async function bootstrap(): Promise<Server> { const expressApp = express(); const app = await createExpressApp(expressApp); await app.init(); return createServer(expressApp); } export async function handler(event: any, context: Context): Promise<Response> { if (!cachedServer) { const server = await bootstrap(); cachedServer = server; } return proxy(cachedServer, event, context, 'PROMISE').promise; }
At this point, we can run our NestJS application as an AWS Lambda function. Now, let’s create our app services to add some records to our DynamoDB database.
Create a blog service
Let’s create the CRUD operations for our blog API. First, open up the src/app.service.ts
file and replace the code with the following imports in the code snippet below:
import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { v4 as uuid } from 'uuid'; import * as AWS from 'aws-sdk'; import Blog from './interface';
In the code snippet above, we imported the AWS SDK to interact with our DynamoDB, uuid
to generate random strings for our blog ID, and the blog interface, which we’ll create later.
To know which endpoint to connect DynamoDB to, we’ll check the environment in which our application is running:
... const dynamoDB = process.env.IS_OFFLINE ? new AWS.DynamoDB.DocumentClient({ region: "localhost", endpoint: process.env.DYNAMODB_ENDPOINT, }) : new AWS.DynamoDB.DocumentClient(); ...
We’ll use the AWS.DynamoDB.DocumentClient method
to create our CRUD operations with the code snippets below:
@Injectable() export class AppService { async getBlogs(): Promise<any> { try { return dynamoDB .scan({ TableName: "BlogsTable", }) .promise(); } catch (e) { throw new InternalServerErrorException(e); } } async createBlog(blog: Blog): Promise<any> { const blogObj = { id: uuid(), ...blog, }; try { return await dynamoDB .put({ TableName: "BlogsTable", Item: blogObj, }) .promise(); } catch (e) { throw new InternalServerErrorException(e); } } async getBlog(id: string): Promise<any> { try { return await dynamoDB .get({ TableName: process.env.USERS_TABLE_NAME, Key: { id }, }) .promise(); } catch (e) { throw new InternalServerErrorException(e); } } async deleteBlog(id: string): Promise<any> { try { return await dynamoDB .delete({ TableName: "BlogsTable", Key: { todosId: id, }, }) .promise(); } catch (e) { throw new InternalServerErrorException(e); } }
Now, create an interface.ts
file in the src
folder and add the code snippet below:
export default interface Blog { title: string; coverImage: String; body: string; createdBy: string; dateCreated: string; }
Create blog controllers
With our blog services created, let’s define the endpoints to access those services. First, open the src/app.controller.ts
file and replace the current code with the code snippet below:
import { Controller, Get, Post, Delete, Body, Param } from '@nestjs/common'; import { AppService } from './app.service'; import Blog from './interface'; Then define the blog routes with the code snippet below: @Controller('blogs') export class AppController { constructor(private readonly appService: AppService) {} @Get() async getTodos(): Promise<Blog[]> { return await this.appService.getBlogs(); } @Post() async createTodo(@Body() blog: Blog): Promise<Blog> { return await this.appService.createBlog(blog); } @Post(':id') async getTodo(@Param() id: string): Promise<Blog> { return await this.appService.getBlog(id); } @Delete(':id') async deleteTodo(@Param() id: string): Promise<any> { return await this.appService.deleteBlog(id); } }
With our blog routes set up, let’s build and run the application.
Test the application
Let’s run our application with the command below. Since our handler points to the dist
folder, we need to build the application before running it:
npm run build && serverless offline start
If everything goes well, you’ll see the following output on the console, as shown below:
Conclusion
In this tutorial, we learned how to build a NestJS Serverless application on AWS with DynamoDB. First, we reviewed the Serverless Framework, NestJS, and DynamoDB. Then, we created a Blog Lambda function for the demonstrations.
To learn more about the Serverless Framework, be sure to check out the official documentation. I hope you enjoyed this article, and be sure to leave a comment if you have any questions.
The post NestJS serverless application on AWS with DynamoDB appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/vs4wMim
via Read more