# Interceptors

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

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

Creating and consuming an interceptor is a 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

Loading in progress...

# Interceptor class

To create an interceptor class you need to implement the IInterceptor interface and implement the intercept(context: IInterceptorContext<any>, next?: IInterceptorNextHandler) method, and use the @Interceptor() annotation to register your interceptor class. Inside your src/interceptors/MyInterceptor.ts folder, create the following simple interceptor.

import {InterceptorMethods, InterceptorContext, InterceptorNext, Interceptor} from "@tsed/di";

@Interceptor()
export class MyInterceptor implements InterceptorMethods {
  /**
   * 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: InterceptorContext<any>, next: InterceptorNext) {
    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, Injectable} from "@tsed/di";
import {MyInterceptor} from "../interceptors/MyInterceptor";

@Injectable()
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");
  }
}

// on class
@Injectable()
@Intercept(MyInterceptor, "simple data")
export class MyService2 {
  mySimpleMethod1() {
    console.log("the simple method is executed");
  }

  mySimpleMethod2() {
    console.log("the simple method is executed");
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

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

Last Updated: 3/15/2024, 12:53:49 PM

Other topics