Clara Ekekenta Software Engineer and perpetual learner with a passion for OS and expertise in Python, JavaScript, Go, Rust, and Web 3.0.

Writing AWS Lambda middleware with Middy.js

6 min read 1896 107

Writing AWS Lambda Middleware Middy.js

AWS Lambda is an excellent service for developing serverless applications. It enables developers to create functions that can be triggered by a variety of events without the need for server management. You can use middleware to handle common tasks like input validation, error handling, and response formatting to streamline and modularize your Lambda functions.

Middy.js is a popular middleware engine that makes it simple to write and maintain AWS Lambda middleware. In this tutorial, we’ll guide you through the process of writing AWS Lambda middleware using Middy.js. We will demonstrate this by creating a simple blog app. By the end of this tutorial, you will have a deeper understanding of Middy.js and how to use it in your own projects.

Jump ahead:

Prerequisites

To follow along with the tutorial portion of this article, you should have:

  • A basic knowledge of JavaScript and Node.js
  • Familiarity with AWS Lambda
  • A serverless framework installed globally: npm install -g serverless
  • Node.js and npm installed on your local machine

What is Middy.js?

Middleware is a design pattern that allows developers to turn common tasks, like input validation, error handling, and response formatting, into reusable components. Using middleware, we can keep our Lambda functions clean, modular, and easy to maintain.

Middy.js is a middleware engine for AWS Lambda functions that was created specifically for use with Node.js.
It provides a simple interface for incorporating middleware into Lambda functions and a robust ecosystem of inbuilt middleware for handling common tasks.

Why use Middy.js?

There are several reasons to use Middy.js in your AWS Lambda projects:

  • Simplification: Middy.js helps manage different parts of your Lambda functions, by making your code more readable and easier to maintain
  • Modularity: Middy.js helps to encapsulate common tasks into reusable components, enabling you to promote a modular design in your Lambda functions
  • Inbuilt middleware: Middy.js includes a library of inbuilt middleware for common tasks, eliminating the need to write custom middleware for every project
  • Community support: Because Middy.js has a vibrant community, you can expect ongoing development, bug fixes, and new features

Simplifying Node.js code with Middy.js

Let’s see how Middy.js can be used to simplify a Node project. We’ll compare a simple AWS Lambda function with Middy.js to one implemented using Node.js middleware.

Here’s a basic Lambda function written without Middy:

exports.handler = async (event, context) => {
  try {
    const body = JSON.parse(event.body);
    const result = await someAsyncOperation(body);
    return {
      statusCode: 200,
      body: JSON.stringify(result),
    };
  } catch (error) {
    console.error(error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Internal server error' }),
    };
  }
};

This function accepts an event and a context as input and performs some asynchronous operations on the data. If there is an error during this process, the function throws a 500 internal server error code.

Now, let’s see how the same function can be written using Middy.js:

const middy = require('middy');
const { jsonBodyParser, httpErrorHandler } = require('middy/middlewares');

const handler = async (event, context) => {
  const body = event.body;
  const result = await someAsyncOperation(body);
  return result;
};

exports.handler = middy(handler)
  .use(jsonBodyParser())
  .use(httpErrorHandler());

In the above example, we parse the event body into JSON using Middy’s jsonBodyParser middleware. We also utilize the httpErrorHandler middleware to handle any errors that may arise during the function’s execution.

By using Middy, we eliminate the try-catch block and also no longer need to explicitly handle problem responses because the httpErrorHandler middleware handles this for us. We also benefit from Middy’s other capabilities such as its automated JSON serialization and its flexibility to add more middleware, as needed.

Middy.js vs. other Node.js middleware engines

Express.js and Koa.js are other Node.js middleware engines that could be included for a project. However, these engines are primarily intended for traditional server-based applications, rather than serverless applications hosted on AWS Lambda.

Middy.js is specifically tailored for AWS Lambda and serverless applications, while Express and Koa are popular frameworks for creating server-based applications using Node.js. Despite their differing focuses, it is possible to compare their functionalities.

Express is a lightweight, extensible framework that allows for the rapid development of web applications and APIs. It is well-established, widely used, and possesses a vast ecosystem of middleware to expand its capabilities. Koa, in contrast, is a modern, lightweight framework developed by the same team that created Express. Koa takes advantage of ECMAScript’s async/await feature, enabling a more elegant and concise approach to write asynchronous code.



Express and Koa both prioritize modularity and include inbuilt middleware for common tasks, similar to Middy.js. However, their primary purpose centers on server-based applications instead of serverless architectures. Middy.js is designed for AWS Lambda, whereas Express and Koa can be utilized across various hosting platforms and server environments.

Now, let’s jump into our tutorial and see Middy in action!

Setting up a Middy.js project

To start, create a new directory for the project and navigate to it in the terminal, like so:

mkdir middy-blog-app
cd middy-blog-app

Next, initialize a new Node.js project:

npm init -y

Then, install Middy.js with other dependencies:

npm install @middy/core @middy/http-json-body-parser @middy/http-urlencode-body-parser

Creating a Lambda function with Middy.js

Next, we’ll create a Lambda function to handle requests for creating or retrieving blog posts. Start by creating a new file named index.js in the project directory. This file will hold the code for our AWS Lambda function.

Import Middy.js and the AWS SDK:

const middy = require('@middy/core');

Next, create a basic Lambda function that to handle the HTTP requests:

const blogPosts = []; // Use an array to store blog posts

const handler = async (event, context) => {
  const { httpMethod, body } = event;

  if (httpMethod === 'POST') {
    // Handle creating a blog post
    const { title, content } = body;

    const newPost = {
      id: Date.now().toString(),
      title,
      content,
      createdAt: new Date().toISOString(),
    };

    blogPosts.push(newPost);

    return {
      statusCode: 201,
      body: JSON.stringify({ message: 'Blog post created successfully', post: newPost }),
    };
  } else if (httpMethod === 'GET') {
    // Handle retrieving blog posts
    return {
      statusCode: 200,
      body: JSON.stringify(blogPosts),
    };
  } else {
    // Return an error for unsupported HTTP methods
    return {
      statusCode: 405,
      body: JSON.stringify({ message: 'Method Not Allowed' }),
    };
  }
};

In the above Lambda function, we define a blogPosts array variable to store the blog details. Then we use the httpMethod object from the events parameter to check the type of request made by the user to either return the blog post or to add a new one. If the request is not a GET or POST request, we return a 405 error with the error message: Method Not Allowed.

Next, wrap the handler function with Middy to enable the middleware:

const middyHandler = middy(handler);

Finally, export the Middy-wrapped Lambda function:

exports.handler = middyHandler;

Writing custom middleware

Now, we’ll create custom middleware for input validation and error handling.

First, create a middleware.js file in the project folder. Then, in the middleware.js file, create an inputValidationMiddleware function that validates the incoming blog post data:

  const inputValidationMiddleware = () => {
    return {
      before: async (handler) => {
        const { httpMethod, body } = handler.event;
        if (httpMethod === "POST") {
          const parsedBody = JSON.parse(body);
          if (!parsedBody.title || !parsedBody.content) {
            throw new Error("Invalid input: Title and content are required");
          }
          handler.event.body = parsedBody;
        }
      },
    };
  };

In the above code, we create middleware that gets called before hitting the handler function to parse the request payload and check if the expected fields exist in the request payload.

Next, create an errorHandlingMiddleware function that captures any errors thrown during the execution of the Lambda function. Add the following code to the middleware.js file:

const errorHandlingMiddleware = () => {
  return {
    onError: async (handler, next) => {
      const { error } = handler;
      console.error('Error:', error);

      handler.response = {
        statusCode: error.statusCode || 500,
        body: JSON.stringify({
          message: error.message || 'Internal Server Error',
        }),
      };

      return next();
    },
  };
};

In the above code, we define a middleware function, errorHandlingMiddleware, that has an onError method to handle errors that occur during the request/response cycle. When an error occurs, the method logs the error message to the console, creates a response object with a status code and message, and passes control to the next middleware function in the cycle.

The status code defaults to 500 internal server error code if the error object doesn’t have a statusCode property. The error message is included in the response body as a JSON string.


More great articles from LogRocket:


Now, import and add the custom middleware to the Middy-wrapped Lambda function:

...
const { errorHandlingMiddleware, inputValidationMiddleware } = require('./middleware');
...

middyHandler
.use(inputValidationMiddleware())
.use(errorHandlingMiddleware());

Using the Middy.js inbuilt middleware

Middy.js provides a set of inbuilt middleware for common tasks. We’ll use the inbuilt jsonBodyParser middleware to automatically parse JSON request bodies and the urlencodedBodyParser middleware to parse form data.

First, import the jsonBodyParser and urlEncodeBodyParser middleware:

const { jsonBodyParser } = require('@middy/http-json-body-parser');
const { urlEncodeBodyParser } = require('@middy/http-urlencode-body-parser');

Next, add the jsonBodyParser and urlEncodedBodyParser middleware to the Middy-wrapped Lambda function:

middyHandler
  ...
  .use(jsonBodyParser())
  .use(urlEncodeBodyParser())
  ...

Middy’s integrated middleware significantly simplifies the management and processing of a variety of request payloads. For example, The jsonBodyParser middleware automatically parses incoming JSON request bodies and the urlEncodedBodyParser middleware handles form data submitted with the application/x-www-form-urlencoded content type. This enables the application to efficiently handle different kinds of data.

By using these two types of middleware, we can easily handle and process different types of request payloads without writing any additional code for parsing.

Testing the Lambda function

To test our Lambda function locally, we can use the AWS SAM CLI or a similar tool.

Install the AWS SAM CLI, like so:

npm install --save-dev serverless-offline

Next, create a serverless.yaml file in the project directory with the below content:

service: middy-blog-app
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs14.x

functions:
  middyBlogFunction:
    handler: index.handler
    events:
      - http:
          path: /blog
          method: any
          cors: true

plugins:
  - serverless-offline

Finally, start the local API with the following command:

serverless offline

Once the above command is run, you should see output in your console similar to that shown below:

Console Output Lambda Functions Middy.js

You can now send HTTP requests to your Lambda function using your browser or a tool like curl or Postman.

For example, you can use curl to send a GET request:

curl http://localhost:3000/blog

Or, you can send a POST request with JSON data:

curl -X POST -H "Content-Type: application/json" -d '{"title": "Test Post", "content": "This is a test post."}' http://localhost:3000/blog 

Conclusion

In this tutorial, we created a simple blog app to demonstrate how to write AWS Lambda middleware using Middy.js. We covered the basics of setting up a project, creating a Lambda function with Middy, writing custom middleware, using inbuilt Middy middleware, testing the Lambda function, and deploying the blog app.

After following along with this tutorial, you should have a more comprehensive understanding of Middy.js and how to use it in your own serverless projects. Middleware is a powerful tool that helps you modularize and streamline your AWS Lambda functions, making your code more maintainable and easier to test. Keep exploring the Middy.js documentation and inbuilt middleware to discover more ways to enhance your Lambda functions.

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Clara Ekekenta Software Engineer and perpetual learner with a passion for OS and expertise in Python, JavaScript, Go, Rust, and Web 3.0.

Leave a Reply