NodeJS Express and AWS Lambda Functions

NodeJS Express and AWS Lambda Functions

Lambda functions, nodejs and github actions series

In my previous article I wrote about Lambda Function, NodeJS and GitHub Actions. Now, I will share a second article related to "Running a Lambda Express API".

This article assumes that you are familiar with Lambda Function and GitHub Actions. If you are new to Lambda or Github itself, refer first to my previous article call Lambda Functions Challenge.

For this article, you will need:

  • AWS credential for deploy and execution access.

  • GitHub Repository

  • Repository secret with AWS credential loaded: Repository Settings -> Secrets and Variables -> Actions -> New repository secret (we need it for deploy lambda function).

Working with NodeJS

Create express API

Use the npm command to create a new project:

npm init --yes # this will trigger automatically populated initialization with default values

After that, we are going to install express framework to build the REST API:

npm install express

Next step, create a new app directory and inside create an app.js file. The project should look like this:

In the app.js file we add the express server setup:

const express = require('express')
const app = express();
const port = process.env.PORT || 3000;

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get('/api/v1/hello', (req, res) => {
  res.send({ app: 'aws-lambda-expressjs-nodejs', version: '1.0.0' });
});

app.listen(port, () => console.log(`Listening on: ${port}`));

Execute the following command to check that everything is ok:

node app/app.js

Then, visit the url http://127.0.0.1:3000/api/v1/hello and you should get the response bellow:

{
  "app": "aws-lambda-expressjs-nodejs",
  "version": "1.0.0"
}

Convert our express api to "Lambda Express REST API"

We need to install serverless-http our "serverless wrapper" to make our express app ready to deploy on Lambda Environment:

npm install serverless-http

This module allows you to 'wrap' your API for serverless use. No HTTP server, no ports or sockets. Just your code in the same execution pipeline you are already familiar with

Now, we are going to add the serverless module and creating a handler for lambda function:

const express = require('express')
// include the serverless-http module
const serverless = require('serverless-http');

const app = express();
const port = process.env.PORT || 3000;

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get('/api/v1/hello', (req, res) => {
  res.send({ app: 'aws-nodejs-lambda-function', version: '1.0.0' });
});

// comment express server
//app.listen(port, () => console.log(`Listening on: ${port}`));

// add the handler for our lambda function
module.exports.handler = serverless(app);

From now on, our project is ready to deploy in AWS Lambda environment but before we need to change the function runtime settings and add a trigger type AWS API Gateway (REST).

Runtime Settings

We should be set the handler value with "app/app.handler" since our handler function is located under de directory "app" in "app.js" file.

Go to "Run Time settings" and set the handler value with app/app.js

API Gateway (REST API)

Create a trigger for add an API Gateway that route HTTP requests to our Lambda function:

Now, we are going to create a resources in our API Gateway. Go to the API Gateway page, click on Action button and select "Create Resource":

On "resource path" configuring /{proxy+} as a proxy resource catches all requests to its sub-resources.

Right after that, create an integration between api gateway and lambda function:

The Api Gateway Resources should look like this:

At this point, we need to deploy a new version of our Api. Go to the Action button and select deploy option:

In the stage editor you can find de invoke url (we need it in the final step):

Create a local environment execution

We need a local environment for develop new features and test the code in our machine. So, in the "scripts" section located the package.json file, add the following command to execute the project as Local Environment using express server:

"local": "APP_ENV=local node app/app.js",

We passed an APP_ENV environment variable with a value equal "local" for set running express out of serverless module.

Now, we will edit the app.js file and make the following changes:

...
if (process.env.APP_ENV === 'local') {
  app.listen(port, () => console.log(`Listening on: ${port}`));
} else {
  module.exports.handler = serverless(app);
}

Execute the following command to check that everything is ok:

npm run local

Then, visit the url http://127.0.0.1:3000/api/v1/hello and you should get the response bellow:

{
  "app": "aws-lambda-expressjs-nodejs",
  "version": "1.0.0"
}

Pipeline

For this article we are going to create a pipeline using GitHub actions. We need to add a main.yml file under the path ".github/workflows" with the content bellow:

\ You should provide the AWS Lambda Function name in the step "Deploy"*

name: Deploy

on:
  push:
    branches:
      - master

jobs:
  deploy-lambda:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '16'
      - name: Install dependencies
        run: npm ci --ignore-scripts
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1-node16
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}
      - name: Deploy
        run: |
         zip -r deploy.zip .
         aws lambda update-function-code --function-name=your-lambda-function --zip-file=fileb://deploy.zip

Now we are all set to deploy our application. So, commit and push the changes to the repository and the pipeline starts automatically. You can see the progress in the Actions option:

Now, is time to test our lambda function. Go to the browser, paste de api gateway url and add our express api path:

https://xxxxxx.execute-api.us-east-1.amazonaws.com/default/api/v1/hello

You should get the response bellow:

}
  "app": "aws-lambda-expressjs-nodejs",
  "version": "1.0.0"
}

Well done, we have a express js running over lambda function 👏 👏

Conclusion

You can get the source files from my GitHub repository and my previous article here

For this very simple example, we used the default values provided from AWS Console. We didn't talk about Api Gateway security (token, certificates, api key, etc) and Lambda functions deployment packages size as well; If our project is larger than 50 MB, we need a extra integration with S3 Bucket.

Fortunately for us, Lambda functions and api gateway has a lot of features and it is well-documented.

Resources

https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-deploy-api.html

https://www.npmjs.com/package/serverless-http

https://www.npmjs.com/package/express

htps://www.npmjs.com/package/express

https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-package.html

https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-package.html

https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html

Did you find this article valuable?

Support Max Martínez Cartagena by becoming a sponsor. Any amount is appreciated!