Interceptors

An interceptor is a class annotated with the decorator. Interceptors should implement the interface.

Interceptors have a set of useful capabilities which are inspired by the Aspect Oriented Programming (AOP) technique.

Creating and consuming an interceptor is two-step process:

  1. Create and annotate the interceptor class that will intercept calls to service methods
  2. Decide which methods will be intercepted by which interceptor

They make it possible to:

  • bind extra logic before / after method execution
  • transform the result returned from a function
  • transform the exception thrown from a function
  • extend the basic function behavior
  • completely override a function depending on specific conditions

Decorators

    Interceptor class

    To create interceptor class you need to implement he IInterceptor interface and implement the aroundInvoke(ctx: IInterceptorContext) method, and use the @Interceptor() annotation to register your interceptor class. Inside your src/interceptors/MyInterceptor.ts folder create the following simple interceptor.

    import {IInterceptor, IInterceptorContext, IInterceptorNextHandler, Interceptor} from "@tsed/common";
    
    @Interceptor()
    export class MyInterceptor implements IInterceptor {
      /**
       * ctx: The context that holds the dynamic data related to the method execution and the proceed method
       * to proceed with the original method execution
       *
       * opts: Static params that can be provided when the interceptor is attached to a specific method
       */
      async intercept(context: IInterceptorContext<any>, next: IInterceptorNextHandler) {
        console.log(`the method ${context.propertyKey} will be executed with args ${context.args} and static data ${context.options}`);
        // let the original method by calling next function
        const result = await next();
    
        console.log(`the method was executed, and returned ${result}`);
    
        // must return the returned value back to the caller
        return result;
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    Use the interceptor

    Now that your interceptor logic is in place you can use it in any other service. You need to use the @Intercept(InterceptorClass, opts) annotation to register which interceptor should be used for the specific method you want to intercept. An example service in src/services/MyService.ts:

    import {Intercept} from "@tsed/common";
    import {MyInterceptor} from "../interceptors/MyInterceptor";
    
    export class MyService {
      // MyInterceptor is going to be used to intercept this method whenever called
      // 'simple data' is static data that will be passed as second arg to the interceptor.intercept method
      @Intercept(MyInterceptor, "simple data")
      mySimpleMethod() {
        console.log("the simple method is executed");
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    If the service method is executed like myServiceInstance.mySimpleMethod() we will get the following output:

    the method mySimpleMethod will be executed with args and static data simple data
    the simple method is executed
    the method was executed, and returned undefined
    
    1
    2
    3