Hamburger Icon
  • Labs icon Lab
  • Core Tech
Labs

Guided: Middleware and Interceptors in NestJS

Ready to level up your NestJS skills? Join this hands-on lab and discover the power of middleware and interceptors. Through interactive exercises, you'll learn how to enhance application functionality, optimize processes, and implement robust logging and monitoring. Gain practical insights into the request/response lifecycle while building scalable, maintainable applications.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 37m
Published
Clock icon Feb 28, 2025

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Introduction

    In this lab, you’ll explore the fundamentals of Middleware and Interceptors in NestJS. You’ll learn their purposes, common use cases, and how they fit into the request-response lifecycle. This foundational knowledge will prepare you for the hands-on steps that follow.

    Middleware Overview

    Middleware is a function that runs before your request reaches the controller. You can use it to handle cross-cutting concerns during the request-response cycle.

    Common Middleware Use Cases:
    • Logging incoming requests
    • Authenticating users
    • Modifying request objects
    • Enabling features like CORS or rate limiting

    Interceptor Overview

    Interceptors can transform request and response data, handle exceptions, and implement reusable logic before or after method execution. Think of them as powerful tools for modifying how data flows through your application.

    Common Interceptor Use Cases:
    • Measuring and logging response times
    • Transforming outgoing response data
    • Catching and handling exceptions globally
    • Implementing caching strategies

    What You’ll Do Next

    In the next steps, you’ll:

    • Create a custom logging middleware to monitor incoming requests.
    • Develop a global exception interceptor to manage application-wide errors.
    • Build a null response transformer interceptor to handle and clean up null responses.

    Click the Next step arrow below to dive in and create a custom logging middleware to see how Middleware works in practice!

  2. Challenge

    Create Custom Logging Middleware

    Create Custom Logging Middleware

    In this step, you will create middleware that logs incoming HTTP requests. Middleware in NestJS executes before route handlers, making it ideal for tasks like logging, request validation, and setting headers.

    Create a LoggerMiddleware 

    Open the file: src/middleware/logger.middleware.ts

    Overview of Required Imports:
    • NestMiddleware: An interface that enforces a use method for handling incoming requests before they reach route handlers.
    • Request and Response: Express TypeScript types and methods to access the request data and manipulate the response.
    • NextFunction: A callback that passes control to the next middleware or route handler.
    You will use these classes to build a middleware to log all incoming requests to the console. ### Register Middleware in AppModule

    After creating your middleware, you must register it at the module level. For this exercise, you’ll register it in AppModule for global application.

    The following tasks will be applied to the src/app.module.ts file.

    Note that using the start:dev command will run the application in watch mode. So any changes made in the following steps will automatically compile your changes.

    You have successfully created and configured your first middleware in NestJS.

    Next, you'll explore to how implement Interceptors for finer grained control over the data flow.

  3. Challenge

    Create Custom Global Exception Interceptor

    Create Custom Global Exception Interceptor

    In this step, you will create a global exception interceptor to handle uncaught errors consistently across your application.

    While middleware can handle some request and response processing, it cannot handle exceptions thrown during route handling or after the response has been sent.

    Interceptors can manipulate incoming requests and outgoing responses, making them ideal for logging, transforming data, and handling exceptions globally.

    Creating the GlobalInterceptor

    Open the file: src/interceptors/global.interceptor.ts

    Overview of Required Imports ----- - **NestInterceptor**: An interface that allows you to implement custom logic to intercept and modify incoming requests and outgoing responses, making it ideal for tasks like logging, transformation, and exception handling. 
    • ExecutionContext: Provides details about the current execution process, allowing access to request and response objects, route handlers, and other context-specific information.

    • CallHandler: Provides access to the next handler in the request pipeline, allowing the interceptor to control the flow and handle the response stream.

    • Observable: Represents a stream of data that can emit multiple values over time, allowing the interceptor to handle asynchronous operations like HTTP responses. This comes from the RxJS library, which provides powerful tools for reactive programming and handling asynchronous data streams in NestJS. 

    • catchError and throwError: These RxJS operators are used to catch errors in the response stream and rethrow them with a custom structured error object, enabling the interceptor to handle exceptions and return consistent error responses.: 

    • InternalServerErrorException: A built-in exception class provided by NestJS, used to indicate that an unexpected server error has occurred (HTTP status code 500).


    Building the Interceptor

    You will use these classes to build an Interceptor to catch unhandled errors and rethrow a user friendly message to the client. ## Register the Interceptor Globally

    Now that the interceptor is ready, it needs to be registered with the NestJS application. This can be accomplished in several places. Since this interceptor does not require any dependencies it can be registered in the main.ts bootstrap file.  In addition to this global scope, Interceptors can also be applied directly to individual endpoints. This will be explored in the next step.

  4. Challenge

    Create Custom Endpoint Interceptor

    Create Custom Endpoint Interceptor

    In the previous step, you created a global interceptor to handle unhandled exceptions across the entire application. However, there are situations where you need more control over when and where an interceptor is applied.

    In this step, you will create an interceptor that specifically targets certain endpoints. This interceptor will transform null responses into a custom message or alternative value, ensuring your API consistently provides meaningful responses instead of returning null, which can enhance the client-side experience.


    Creating the NullInterceptor:

    Open the file: src/interceptors/null.interceptor.ts.

    Interceptors are developed the same way no matter where they are registered. This boilerplate code has been provided for you based on what you built in the previous step.

    In this step, you will work with two other RxJS operators: tap and map. ### Apply the Interceptor To apply this NullInterceptor to a specific route, open the controller file: src/app.controller.ts.

    Overview of Required Imports - **UseInterceptors** The `@UseInterceptors` decorator applies one or more interceptors to a controller or route, allowing you to modify requests or responses, log data, handle exceptions, or transform results. You can apply it at the class level for all routes or at the method level for a specific route. - **NullInterceptor** You also need to import the interceptors that you would like to apply
    Great work in implementing this interceptor!

    Before completing this lab, there’s one more interceptor configuration option you should explore in the next step.

  5. Challenge

    Configure NullInterceptor at the Module Level

    Configure NullInterceptor at the Module Level

    Previously, you configured interceptors at both the application bootstrap and controller levels. In this step, you will configure the same NullInterceptor at the module level using the providers array. This method applies the interceptor globally within the module, eliminating the need to decorate individual controllers. Additionally, when your interceptor requires dependency injection, registering it in the module allows NestJS's built-in IoC (Inversion of Control) container to manage those dependencies effectively.

    Apply the Interceptor at the Module

    Open the file: src/app.module.ts

    Overview of Required Imports - **APP_INTERCEPTOR**: A built-in token provided by NestJS that allows you to register an interceptor globally at the module level. By using this token in the providers array, you instruct NestJS to apply the specified interceptor to all incoming requests. - **NullInterceptor**: You also need to import the interceptors that you would like to apply.
    ### Congratulations Congratulations on completing the lab!

    You covered essential NestJS concepts of middleware and interceptors. With these skills, you're ready to build scalable, maintainable NestJS applications. Great job!

Jeff Hopper is a polyglot solution developer with over 20 years of experience across several business domains. He has enjoyed many of those years focusing on the .Net stack.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.