This article will be about the Interceptor design pattern and its usage via Ts.ED. In brief, the pattern injects some logical functionality before and/or after function execution. It has the same nature as the Middleware, but instead of working with a request pipeline, the interceptor handle function invocation and can repeat function execution with different parameters. Let’s assume the scenario where it could be helpful. I have a service that sends requests to some black box. And the black box is not so friendly and in case of exception very possible, there will be only an HTTP status code without any explanation. In case of receiving a 403 status code most probably need to try re-authenticate the user and repeat the request to the service. So basic retry mechanism with the request will not suit this scenario, because there could be multiple different endpoints. It could look like this: async createPermamentCell(shapeId: number): Promise<Cell> { try { return await this.createCell(`shape-${shapeId}`, true); } catch (error: any) { // try auth again if (error.status === 403) { const userEmail = httpContext.get(REQUEST_USER); const token = await this.authenticate(userEmail); httpContext.set(CLIENT, new Client({ token: token, }) ); return await this.createCell(`shape-${shapeId}`, true); } } } // ...As well for other functions same try catch Or like this: async retryOn403(functionToCall: (...args: any[]) => any, ...args: any[]): Promise<Cell> { try { await functionToCall(...args); } catch (error: any) { // try auth again if (error.status === 403) { const userEmail = httpContext.get(REQUEST_USER); const token = await this.authenticate(userEmail); httpContext.set(CLIENT, new Client({ token: token, }) ); return await functionToCall(...args); } } } // The function will be wrapped But these things can get out of control and start to live their own life. In other words, such code will be more difficult to maintain. Changes in part of the code will cause a change in other parts of the code, or vice versa changing in one place reshapes the code that is expected not to be touched. Let’s implement the same logic via the Interceptor approach. And for the example, I will use Ts.ED framework. Of course, other frameworks also contain this mechanism out of the box like in Nestjs or .Net, e.t.c. I require to create a new class and implement the interface and use decorator . InterceptorMethods @Interceptor() @Interceptor() export class ReAuthInterceptor implements InterceptorMethods { constructor() {} async intercept(context: InterceptorContext<any>, next: InterceptorNext) { try { // It's possible to do somthing before call const result = await next(); // Or right after the call return result; } catch (error: any) { if (error.status === 403) { try { const userEmail = httpContext.get(REQUEST_USER); const token = await this.authenticate(userEmail); httpContext.set(CLIENT, new Client({ token: token, }) ); return next(); } catch (error: any) { _log.warn('Failed re auth', error); } // In case of exception on re auth return original exception return next(error); } // All other cases immediately return error return next(error); } } } And any service or particular function can be decorated with the new interceptor . @Intercept(ReAuthInterceptor) @Intercept(ReAuthInterceptor) export class CellService implements CellService { constructor(@Inject(ClientProvider) private clientProvider: ClientProvider) {} // ... Many fucntions } In the parameter of interceptor you can find arguments or (the second parameter that you may use in the decorator . It could help to implement different logic for different functions. context options @Intercept(ReAuthInterceptor, ‘My opts’) There is when you will try to use the interceptor in Ts.ED on the whole class with async and sync functions, e.g.: one specific feature @Intercept(ReAuthInterceptor) export class CellService implements CellService { constructor(@Inject(ClientProvider) private clientProvider: ClientProvider) {} async createCell() { // ... } getCurrentCell(): string { // ... return `some str`; } } The actual result of the second function will be not the , instead of this type, it will be . getCurrentCell() string Promise<string> But otherwise, the code looks more understandable and robust via approach, all things in one place, you can still process the functions differently, and test logic in one place. Intercept