Davide Talesco
Davide Talesco

Reputation: 212

Clarification regarding NestJS Interceptors order of execution

I have a controller handler that return a string.

// Controller.ts

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { UpperInterceptor } from './upper.interceptor';
import { LowerInterceptor } from './lower.interceptor';

@Controller()
export class AppController {

  @Get()
  @UseInterceptors(LowerInterceptor, UpperInterceptor)
  getHello(): string {
    return 'Hello'
  }
}

I have attached 2 interceptors, Lower and Upper which do change the string case accordingly to their name.

// lower.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'

@Injectable()
export class LowerInterceptor implements NestInterceptor {

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('lower')
    return next
      .handle()
      .pipe(
        map((e) => {
          console.log('calling lowercase')
          return e.toLowerCase()
        }),
      );
  }
}
// upper.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'

@Injectable()
export class UpperInterceptor implements NestInterceptor {

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('upper')
    return next
      .handle()
      .pipe(
        map((e) => {
          console.log('calling uppercase')
          return e.toUpperCase()
        }),
      );
  }
}

I would expect the return value to be HELLO but it is hello

As far as I can see Lower interceptor is executed first, but the map that it pipes to the observable is executed after the map of the Upper interceptor and the return string is hence hello.

Do you know why the interceptors are executed in order as expected but the callback they map through pipe is executed in inverted order?

Is this related to nest or just rxjs? Sorry but I am new to both.

Upvotes: 5

Views: 5644

Answers (2)

Sebi2020
Sebi2020

Reputation: 2160

The order is defined by the order of the decorator arguments @UseInterceptors(...). It works like a function call stack in recursive calls. After executing the handler (return from the function), the call stack is unwound again:

1. Lower Interceptor
2.   Upper Intereptor
3.     next.handle()
4.   Upper Interceptor (Transform to uppercase)
5. Lower Interceptor (Transform to lowercase)

Upvotes: 1

Jay McDoniel
Jay McDoniel

Reputation: 70191

It's an RxJS thing. This Stackblitz essentially shows what's going on, even if not explicitly written in your server. Each interceptor is getting chained into the next one. The interceptors follow a First In, Last Out stack frame, so you see lower being logged before upper, but then you see the output as lowercase instead of uppercase like you originally expected. I've added in some tap methods as well to illustrate where in the call chain everything happens with Observbales.

Upvotes: 2

Related Questions