DEV Community

Cover image for Deploy & Run your Node.js Framework App on AWS Lambda with AWS Lambda Web Adapter
Jimmy for AWS Community Builders

Posted on • Updated on

Deploy & Run your Node.js Framework App on AWS Lambda with AWS Lambda Web Adapter

In this article, we'll explore the process of deploying and running a serverless application on AWS Lambda built with Node.js framework such as Hapi.js and NoSQL database like Dynamodb.

In this article, we will use the application that we previously built from this article.

We will use AWS Lambda Web Adapter to run our Node.js Application on AWS Lambda.

AWS Lambda Web Adapter allows developers to build web apps (http api) with familiar frameworks (e.g. Express.js, Next.js, Flask, SpringBoot, ASP.NET and Laravel, anything speaks HTTP 1.1/1.0) and run it on AWS Lambda.

Follow me for more

FullStack Saiyan | Twitter, Instagram, Facebook | Linktree

Share about Web, Mobile, Backend, Frontend, and Cloud Computing.

favicon linktr.ee

AWS Lambda

AWS Lambda is a serverless computing service provided by Amazon Web Services (AWS). It allows you to run code without having to provision or manage servers. With Lambda, you can focus on writing your application code and let AWS handle the infrastructure management, scaling, and server maintenance.

Amazon DynamoDB

Amazon DynamoDB is a fully managed NoSQL database service provided by Amazon Web Services (AWS). It is designed to provide fast and scalable performance for applications that require flexible and reliable data storage. DynamoDB is particularly well-suited for web and mobile applications, gaming backends, content management systems, and other use cases that need low-latency, highly available, and scalable databases.

AWS Serverless Application Model

AWS SAM (Serverless Application Model) is an open-source framework provided by Amazon Web Services (AWS) for building serverless applications. It extends AWS CloudFormation, which is AWS's infrastructure as code service, to provide a simplified way of defining the resources and configurations needed to deploy serverless applications on AWS.

Node.js

Node.js is an open-source, server-side runtime environment that allows developers to build and run server-side applications using JavaScript. It is built on the V8 JavaScript engine, which is the same engine used in Google Chrome, and it enables the execution of JavaScript code on the server, rather than just in web browsers.

Node.js Framework

Node.js has a rich ecosystem of frameworks and libraries to simplify web application development. Some popular Node.js frameworks and libraries include:

  1. Express.js
  2. Koa.js
  3. Sails.js
  4. Hapi.js
  5. Nest.js

In this article, we will deploy and run our application built with Hapi.js and Amazon DynamoDB.

Prerequisites Tools

Table of Contents

Project Folder Structure

Our project folder structure will looks like this

.
|-- app
|   |-- src
|   `-- Dockerfile
`-- template.yaml
Enter fullscreen mode Exit fullscreen mode
  • src is a folder that will houses our Node.js application.
  • Dockerfile is a text file used to define a set of instructions for building our Docker container image.
  • template.yaml is an AWS SAM template, providing a streamlined layer on top of CloudFormation, simplifying the definition and deployment of our serverless applications.

Dockerfile

This is how our Dockerfile looks like

FROM public.ecr.aws/docker/library/node:16.13.2-stretch-slim
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter
EXPOSE 8080
WORKDIR "/var/task"
ADD src/package.json /var/task/package.json
ADD src/package-lock.json /var/task/package-lock.json
RUN npm install --omit=dev
ADD src/ /var/task
CMD ["node", "index.js"]
Enter fullscreen mode Exit fullscreen mode

This is a Dockerfile that defines the build instructions for a Docker image. The image is based on the node:16.13.2-stretch-slim image from Docker Hub, which is a lightweight version of the Node.js runtime environment.

To use Lambda Web Adapter with docker images, add one line to copy Lambda Web Adapter binary to /opt/extensions inside your container.

The Dockerfile starts by copying the lambda-adapter binary from another Docker image (public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1) to the /opt/extensions/lambda-adapter directory in the new image.

The EXPOSE instruction is used to indicate that the container will listen on port 8080. This does not actually publish the port, but it is a useful hint for anyone who wants to run the container.

The WORKDIR instruction sets the working directory for subsequent instructions to /var/task. This is the directory where the Lambda function code will be located.

The ADD instructions copy the package.json, package-lock.json, and source code files from the src directory to the /var/task directory in the image. The npm install command is then run to install the dependencies specified in package.json. The --omit=dev flag is used to exclude development dependencies from the installation, which can help reduce the size of the final image.

Finally, the CMD instruction specifies the command that should be run when a container is started from the image. In this case, it runs the node command with index.js as the argument, which is the entry point for the Lambda function.

SAM Template

AWS SAM (Serverless Application Model) template is a JSON or YAML configuration file used to define the AWS resources and configurations required to deploy a serverless application on Amazon Web Services (AWS).

This is how our complete AWS SAM template will look like.

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Todo Application

Resources:
  TodoFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      MemorySize: 1024
      Timeout: 30
      Environment:
        Variables:
          RUST_LOG: info
          TABLE_NAME: !Ref TodoTable
      Events:
        Root:
          Type: HttpApi
          Properties:
            Path: /
            Method: ANY
        Petstore:
          Type: HttpApi
          Properties:
            Path: /{proxy+}
            Method: ANY
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref TodoTable
    Metadata:
      DockerTag: v1
      DockerContext: ./app
      Dockerfile: Dockerfile

  TodoTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: id
        Type: String

Outputs:
  TodoApi:
    Description: "API Gateway endpoint URL for Prod stage for Todo function"
    Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/"
Enter fullscreen mode Exit fullscreen mode

First, we defined our Lambda Function resource like this in our template.

TodoFunction:
  Type: AWS::Serverless::Function
  Properties:
    PackageType: Image
    MemorySize: 1024
    Timeout: 30
    Environment:
      Variables:
        RUST_LOG: info
        TABLE_NAME: !Ref TodoTable
    Events:
      Root:
        Type: HttpApi
        Properties:
          Path: /
          Method: ANY
      Petstore:
        Type: HttpApi
        Properties:
          Path: /{proxy+}
          Method: ANY
    Policies:
      - DynamoDBCrudPolicy:
          TableName: !Ref TodoTable
  Metadata:
    DockerTag: v1
    DockerContext: ./app
    Dockerfile: Dockerfile
Enter fullscreen mode Exit fullscreen mode

The above template part defines an AWS Lambda function named TodoFunction that is used to handle HTTP requests for a RESTful API.

The Type property specifies that this resource is an AWS::Serverless::Function, which is a type of AWS Lambda function that can be defined using the SAM template format.

The Properties section contains various configuration options for the function. The PackageType property is set to Image, which means that the function will be packaged as a Docker image. The MemorySize property is set to 1024 MB, which is the amount of memory that will be allocated to the function when it runs. The Timeout property is set to 30 seconds, which is the maximum amount of time that the function can run before it is terminated.

The Environment section contains environment variables that will be available to the function at runtime. In this case, there are two variables defined: RUST_LOG and TABLE_NAME. The RUST_LOG variable is set to info, which is a logging level for the Rust programming language. The TABLE_NAME variable is set to the name of a DynamoDB table that the function will interact with. The !Ref function is used to reference the TodoTable resource defined elsewhere in the template.

The Events section defines the events that will trigger the function. In this case, there are two events defined: Root and Petstore. Both events are of type HttpApi, which means that they will be triggered by HTTP requests. The Path property specifies the URL path that will trigger the event, and the Method property specifies the HTTP method that will trigger the event.

The Policies section defines the IAM policies that are attached to the function. In this case, there is one policy defined: DynamoDBCrudPolicy. This policy grants the function permissions to perform CRUD (Create, Read, Update, Delete) operations on the DynamoDB table specified in the TableName property. The !Ref function is used to reference the TodoTable resource defined elsewhere in the template.

The Metadata section contains metadata about the function. In this case, it specifies the Docker tag, Docker context, and Dockerfile that will be used to build the Docker image for the function.

Then we create our Amazon DynamoDB table

TodoTable:
  Type: AWS::Serverless::SimpleTable
  Properties:
    PrimaryKey:
      Name: id
      Type: String
Enter fullscreen mode Exit fullscreen mode

The above template part defines an AWS DynamoDB table named TodoTable that will be used to store data for a RESTful API.

The Type property specifies that this resource is an AWS::Serverless::SimpleTable, which is a type of DynamoDB table that can be defined using the SAM template format.

The Properties section contains various configuration options for the table. The PrimaryKey property specifies the primary key for the table, which consists of a single attribute named id of type String. This means that the id attribute will be used as the partition key for the table.

Last, we defined our function's API Gateway endpoint URL output

Outputs:
  NodejsApi:
    Description: "API Gateway endpoint URL for Prod stage for Todo function"
    Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/"
Enter fullscreen mode Exit fullscreen mode

It defines an output named TodoApi that provides the URL for the API Gateway endpoint for the TodoFunction resource.

The Outputs section of the template is used to define values that can be exported from the stack. These values can be used by other stacks or by external applications to interact with the resources in the stack.

The TodoApi output has a Description property that provides a brief description of the output. In this case, it describes the output as the API Gateway endpoint URL for the Prod stage of the TodoFunction resource.

The Value property is a !Sub function that substitutes variables in a string with their corresponding values. In this case, it substitutes the ${ServerlessHttpApi} variable with the name of the HTTP API that is created for the TodoFunction resource, and the ${AWS::Region} and ${AWS::URLSuffix} variables with the AWS region and URL suffix, respectively.

AWS SAM Deploy

To start deploying our serverless application we can run this command in our terminal

sam build
sam deploy --guided
Enter fullscreen mode Exit fullscreen mode

sam deploy --guided is a command provided by the AWS SAM (Serverless Application Model) CLI (Command Line Interface) that launches an interactive, guided deployment process for your serverless application. This command helps you deploy your application with AWS SAM in an easy and user-friendly manner.

When you run sam deploy --guided, you will be asked for some configurations input, which you can fill in like this.

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Found
        Reading default arguments  :  Success

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: hapijs-todoapp
        AWS Region [ap-southeast-1]: ap-southeast-1
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]: y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: Y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: N
        ExpressFunction has no authentication. Is this okay? [y/N]: y
        ExpressFunction has no authentication. Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: Y
        SAM configuration file [samconfig.toml]: samconfig.toml
        SAM configuration environment [default]: default
Enter fullscreen mode Exit fullscreen mode

AWS SAM will start looking for resources needed for deployment we defined in our template.

Looking for resources needed for deployment:

        Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1aa02zp34lw9q
        A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False
         Image repositories: Not found.
         #Managed repositories will be deleted when their functions are removed from the template and deployed
         Create managed ECR repositories for all functions? [Y/n]:

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

eb03fb97f024: Pushed
421b36cec0ce: Pushed
85de5da90a78: Pushed
29f371b320ea: Pushed
f2ca56bec846: Pushed
7c5fbd8aa99c: Pushed
1b4d47b7a3e7: Pushed
a3a67812fd2d: Pushed
cb6798486542: Pushed
cb0076c98b2c: Pushed
8f0b8fdd795f: Pushed
todofunction-4014533baa38-v1: digest: sha256:105bc7c6ce5aadd80e2c6e2fb96a64f2dde41a24600499fc442ac64f7f7f52f0 size: 2623
Enter fullscreen mode Exit fullscreen mode

Then SAM will start initiating deployment

Initiating deployment
=====================

TodoFunction has no authentication.
TodoFunction has no authentication.
        Uploading to hapijs-todoapp/1599f63c1d296a53c88fc482c7d09a71.template  1413 / 1413  (100.00%)


Waiting for changeset to be created..

CloudFormation stack changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                     LogicalResourceId                             ResourceType                                  Replacement
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                         ServerlessHttpApiApiGatewayDefaultStage       AWS::ApiGatewayV2::Stage                      N/A
+ Add                                         ServerlessHttpApi                             AWS::ApiGatewayV2::Api                        N/A
+ Add                                         TodoFunctionPetstorePermission                AWS::Lambda::Permission                       N/A
+ Add                                         TodoFunctionRole                              AWS::IAM::Role                                N/A
+ Add                                         TodoFunctionRootPermission                    AWS::Lambda::Permission                       N/A
+ Add                                         TodoFunction                                  AWS::Lambda::Function                         N/A
+ Add                                         TodoTable                                     AWS::DynamoDB::Table                          N/A
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Then after completing the deployment, AWS SAM will return an output of the endpoint URL like this.

Outputs
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Key                 TodoApi
Description         API Gateway endpoint URL for Prod stage for Todo function
Value               https://fr9mddpdg7.execute-api.ap-southeast-1.amazonaws.com/
Enter fullscreen mode Exit fullscreen mode

We'll initiate testing our application using the output endpoint URL https://fr9mddpdg7.execute-api.ap-southeast-1.amazonaws.com/.

For the next subsequent deployment or after modifying AWS SAM template, you can just run this command.

sam build
sam deploy
Enter fullscreen mode Exit fullscreen mode

Test

To verify the functionality of our Endpoint URL, we can execute the following commands to both send new data and retrieve the corresponding response:

$ curl -k -X POST \
> -H "Content-Type: application/json" \
> -d '{"title":"Deploy and Run Node.js Application on AWS Lambda"}' \
> https://fr9mddpdg7.execute-api.ap-southeast-1.amazonaws.com/todos

{"$metadata":{"httpStatusCode":200,"requestId":"KDTCSC5G29C07JE6TM0V4QH0VRVV4KQNSO5AEMVJF66Q9ASUAAJG","attempts":1,"totalRetryDelay":0}}
Enter fullscreen mode Exit fullscreen mode

Subsequently, we can retrieve the data we previously posted by executing this command and inspecting the response:

$ curl -k  https://fr9mddpdg7.execute-api.ap-southeast-1.amazonaws.com/todos

{"response":[{"id":"1698140034538","iscompleted":false,"title":"Deploy and Run Node.js Application on AWS Lambda"}]}
Enter fullscreen mode Exit fullscreen mode

The above commands demonstrate the successful deployment and execution of our Node.js application on AWS Lambda, connected to an Amazon DynamoDB database.

Cleanup

To remove our serverless application deployed using AWS SAM, execute the following command:

sam delete
Enter fullscreen mode Exit fullscreen mode

sam delete will delete an AWS SAM application by deleting the AWS CloudFormation stack, the artifacts that were packaged and deployed to Amazon S3 and Amazon ECR, and the AWS SAM template file.

Conclusions

In this article, we discover that we can deploy and execute our Node.js application on AWS Lambda, leveraging the capabilities of the AWS Lambda Web Adapter.

Check out my previous post

Top comments (0)