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:
To follow along with the tutorial portion of this article, you should have:
npm install -g serverless
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.
There are several reasons to use Middy.js in your AWS Lambda projects:
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.
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!
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
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;
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.
Now, import and add the custom middleware to the Middy-wrapped Lambda function:
... const { errorHandlingMiddleware, inputValidationMiddleware } = require('./middleware'); ... middyHandler .use(inputValidationMiddleware()) .use(errorHandlingMiddleware());
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.
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:
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
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.
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.
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. Start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]