Middleware

Middleware

미들웨어는 라우터 핸들러 전에 호출되는 함수이다. 기본적으로 express 에 middleware 와 동일하다. class 와 function 모두 사용할 수 있고, class 로 선언하는 경우에는 NestMiddleware 인터페이스를 구현해야 한다.

1
2
3
4
5
6
7
8
9
10
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}

Dependency injection

미들웨어는 Provider 와 Controller 와 마찬가지로 모듈 내에서 사용 가능한 종속성으로 주입할 수 있다.

Applying middleware

middleware 를 갖는 module 은 NestModule를 구현해야 한다. 데코레이터에서 따로 등록할 수 는 없고, configure() 메서드를 통해 등록한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

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

forRoutes 를 두어서 특정 요청에 대한 미들 웨어로 제한할 수도 있다. 원하는 요청에 유형을 참조하기 위해 RequestMethod.GET 과 같이 열거형을 사용한다. 추가적으로 configure() 메서드는 async 키워드 사용이 가능하다.

Route wildcards

패턴 기반의 경로도 지원한다. forRoutes({ path: 'ab*cd', method: RequestMethod.ALL })

Middleware consumer

MidddlewareConsumer 는 helper class이다. midlleware 를 관리하는 내장 메서드를 재공한다. chanid 형태로 호출할 수 있고, forRoutes 는 path, RouteInfo 객체 복수, 단수의 컨트롤러 클래스를 전달할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller';

@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}

apply 메서드 또한 여러 인수를 사용하여 여러 middleware 를 지정할 수 있다.

Excluding routes

.exclude() 메서드에 string or RouteInfo 객체를 지정하여 특정 라우터에서는 middleware 를 제거할 수도 있다.

1
2
3
4
5
6
7
8
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);

exclude() 메서드는 path-to-regexp 패키지를 사용한다.

Functional middleware

함수로도 middleware 를 선언할 수 있다.

1
2
3
4
5
6
import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};

module 에 적용 :

1
2
3
consumer
.apply(logger)
.forRoutes(CatsController);

다른 의존성이 없다면 funcional middleware 를 대안으로 생각해봐라.

Multiple middleware

apply()메서드 안에 순차적으로 제공하여 여러 middleware를 등록시킬 수 있다.

consumer.apply(cors(), helmet(), logger).forRoutes(CatsController)

Global middleware

전역으로 한번에 모든 라우터에서 middleware 를 적용 시키고 싶다면 INestApplication 인스턴스에 use() 메서드를 사용하여 전역으로 등록시킬 수 있다.

1
2
3
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);

글로벌 미들웨어에서 DI 컨테이너에 액세스하는것은 불가능하다. app.use() 를 사용할때 functional middleware 를 사용하거나, AppModule 내에서 .forRoutes(’*’) 와 함께 사용할 수 있다.