# How to Use Middleware in NestJS Applications: Class-Based and Functional Patterns

> Master NestJS middleware with class-based and functional patterns. Learn how to implement custom logic before route handlers efficiently and effectively.

- Repository: [nestjs/nest](https://github.com/nestjs/nest)
- Tags: how-to-guide
- Published: 2026-03-01

---

**NestJS middleware runs before route handlers and is configured through the `MiddlewareConsumer` interface inside a module's `configure()` method, with the framework orchestrating registration and resolution via `MiddlewareModule` in [`packages/core/middleware/middleware-module.ts`](https://github.com/nestjs/nest/blob/main/packages/core/middleware/middleware-module.ts).**

Middleware in NestJS provides a type-safe, testable mechanism to process incoming requests before they reach your controllers. According to the nestjs/nest source code, the framework implements a sophisticated execution pipeline that integrates with the dependency injection container, global exception filters, and request-scoped lifecycles. Learning how to use middleware in NestJS applications enables you to handle cross-cutting concerns like logging, authentication, and request transformation through a clean, declarative API.

## Understanding the NestJS Middleware Architecture

The middleware system centers on the **MiddlewareModule** ([`packages/core/middleware/middleware-module.ts`](https://github.com/nestjs/nest/blob/main/packages/core/middleware/middleware-module.ts)). When your application bootstraps, `MiddlewareModule.register()` executes a four-step process:

1. **Collects** all middleware configurations declared by modules via their `configure()` methods.
2. **Resolves** concrete middleware instances using the `MiddlewareResolver`.
3. **Creates** a proxy that wraps your `use()` method with Nest’s exception-filter handling via `RouterProxy`.
4. **Registers** the resulting handler with the underlying HTTP adapter (Express, Fastify, etc.) for specific routes and methods.

The public API you interact with is the **MiddlewareConsumer** interface ([`packages/common/interfaces/middleware/middleware-consumer.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/middleware/middleware-consumer.interface.ts)), implemented by the **MiddlewareBuilder** ([`packages/core/middleware/builder.ts`](https://github.com/nestjs/nest/blob/main/packages/core/middleware/builder.ts)). This builder exposes a fluent API where `apply()` returns a `ConfigProxy` that stores your middleware list, `exclude()` optionally removes routes from scope, and `forRoutes()` finalizes the configuration into a `MiddlewareConfiguration` object consumed by `MiddlewareModule`.

## Creating Class-Based Middleware

Class-based middleware implements the `NestMiddleware` interface and must be decorated with `@Injectable()` to participate in the dependency injection system.

```typescript
// src/common/middleware/logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log(`Incoming ${req.method} ${req.url}`);
    next();
  }
}

```

The `use` method receives the raw request and response objects from the underlying HTTP server. You must invoke `next()` to pass control to the next middleware in the chain or to the route handler. This pattern mirrors the Express middleware signature while providing full access to NestJS’s DI container for injecting services.

## Registering Middleware with MiddlewareConsumer

To activate middleware, your module must implement the `NestModule` interface and define a `configure()` method. This is where you interact with the `MiddlewareConsumer` to bind middleware to specific routes.

```typescript
// src/app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { LoggerMiddleware } from './common/middleware/logger.middleware';

@Module({
  controllers: [CatsController],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats', '/dogs');
  }
}

```

When `configure()` executes, the `MiddlewareBuilder` creates a `MiddlewareConfiguration` object that `MiddlewareModule` later consumes during the bootstrap phase. The `apply()` method accepts either a single middleware class or an array, while `forRoutes()` accepts strings, route wildcards, or controller classes. Internally, `ConfigProxy` deduplicates overlapping routes to prevent double registration.

## Using Functional Middleware

NestJS also supports plain functional middleware for simple use cases that do not require dependency injection.

```typescript
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';

function requestId(req, res, next) {
  req.id = Date.now();
  next();
}

@Module({})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(requestId).forRoutes('cats');
  }
}

```

Both class-based and functional approaches are valid. The builder flattens nested arrays internally (`apply(...middleware)` calls `middleware.flat()`), allowing you to mix and match styles within a single `apply()` call.

## Advanced Configuration: Exclusions and Request Scoping

### Excluding Specific Routes

Use the `exclude()` method to prevent middleware from running on specific paths or HTTP methods.

```typescript
consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: 'cats/public', method: RequestMethod.GET },
    'health'
  )
  .forRoutes('cats');

```

The `exclude` method accepts either string routes or `RouteInfo` objects containing both path and method. Internally, `ConfigProxy.exclude` expands wildcards and stores them in `excludedRoutes`, which the middleware pipeline filters before final registration.

### Request-Scoped Middleware

For scenarios requiring a fresh instance per request (such as request-specific state or user context), declare middleware with request scope.

```typescript
import { Injectable, NestMiddleware, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class RequestScopeMiddleware implements NestMiddleware {
  use(req, res, next) {
    console.log('Request-scoped instance');
    next();
  }
}

```

When a request arrives, `MiddlewareModule.bindHandler` generates a unique context ID via `ContextIdFactory` and loads a fresh instance through `injector.loadPerContext`. This ensures that request-scoped providers injected into your middleware receive the correct request-scoped instances.

## Summary

- **MiddlewareModule** ([`packages/core/middleware/middleware-module.ts`](https://github.com/nestjs/nest/blob/main/packages/core/middleware/middleware-module.ts)) orchestrates middleware registration during application bootstrap by collecting configurations, resolving instances, and binding them to the HTTP server.
- Configure middleware within a module’s `configure()` method using the **MiddlewareConsumer** fluent API: `apply().forRoutes()`.
- NestJS supports both **class-based** middleware (implementing `NestMiddleware` with `@Injectable()`) and **functional** middleware (plain functions).
- Use `exclude()` to filter routes by path or HTTP method before registration.
- **Request-scoped** middleware creates fresh instances per request via `Scope.REQUEST`, integrated with Nest’s context ID factory and dependency injection system.

## Frequently Asked Questions

### What is the difference between class-based and functional middleware in NestJS?

**Class-based middleware** implements the `NestMiddleware` interface and can leverage NestJS’s dependency injection container to inject services, while **functional middleware** consists of plain JavaScript functions that receive `(req, res, next)` but cannot access the DI container. Both are accepted by `MiddlewareConsumer.apply()`, and the builder internally flattens nested arrays to handle mixed declarations.

### How does NestJS determine middleware execution order?

NestJS executes middleware in the order they are registered within each module’s `configure()` method, following the module import sequence. The `MiddlewareModule` processes configurations sequentially during bootstrap, registering handlers with the underlying HTTP adapter (Express or Fastify) in that same order, creating a deterministic pipeline where earlier registrations run first.

### Can NestJS middleware access the dependency injection container?

Yes, but only **class-based** middleware decorated with `@Injectable()` can access the DI container. When `MiddlewareModule` resolves middleware instances via `MiddlewareResolver`, it instantiates class-based middleware through the NestJS injector, enabling constructor injection of providers. Functional middleware operates outside the container and cannot inject dependencies.

### How do I exclude specific HTTP methods from middleware processing?

Use the `exclude()` method on the `MiddlewareConsumer` chain, passing a `RouteInfo` object that specifies both the path and method: `consumer.apply(Middleware).exclude({ path: 'route', method: RequestMethod.GET }).forRoutes('route')`. Internally, `ConfigProxy.exclude` stores these exclusions in `excludedRoutes`, which the middleware pipeline filters during the registration phase to prevent binding on matching routes.