Repository: nestjs/nest
Pull request #11031 (2023-02-03 10:33:24)

fix(core): unwrap forward ref in module compiler
## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)


## PR Type
What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->
- [x] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: https://github.com/nestjs/nest/issues/10347


## What is the new behavior?


## Does this PR introduce a breaking change?
- [ ] Yes
- [x] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->


## Other information


Patch:
diff --git a/integration/nest-application/global-prefix/src/app.controller.ts b/integration/nest-application/global-prefix/src/app.controller.ts
index d826cd0db..912c0d76c 100644
--- a/integration/nest-application/global-prefix/src/app.controller.ts
+++ b/integration/nest-application/global-prefix/src/app.controller.ts
@@ -7,11 +7,6 @@ export class AppController {
     return 'Hello: ' + req.extras?.data;
   }
 
-  @Get('params')
-  getParams(@Req() req): any {
-    return req.middlewareParams;
-  }
-
   @Get('health')
   getHealth(): string {
     return 'up';
diff --git a/integration/nest-application/global-prefix/src/app.module.ts b/integration/nest-application/global-prefix/src/app.module.ts
index f182915c4..85d1e6ac4 100644
--- a/integration/nest-application/global-prefix/src/app.module.ts
+++ b/integration/nest-application/global-prefix/src/app.module.ts
@@ -22,11 +22,6 @@ export class AppModule {
         req.extras = { data: 'Data attached in middleware' };
         next();
       })
-      .forRoutes({ path: '*', method: RequestMethod.GET })
-      .apply((req, res, next) => {
-        req.middlewareParams = req.params;
-        next();
-      })
       .forRoutes({ path: '*', method: RequestMethod.GET });
   }
 }
diff --git a/packages/common/constants.ts b/packages/common/constants.ts
index f25ddff68..2d18929e0 100644
--- a/packages/common/constants.ts
+++ b/packages/common/constants.ts
@@ -35,7 +35,7 @@ export const ENHANCER_KEY_TO_SUBTYPE_MAP = {
 } as const;
 
 export type EnhancerSubtype =
-  (typeof ENHANCER_KEY_TO_SUBTYPE_MAP)[keyof typeof ENHANCER_KEY_TO_SUBTYPE_MAP];
+  typeof ENHANCER_KEY_TO_SUBTYPE_MAP[keyof typeof ENHANCER_KEY_TO_SUBTYPE_MAP];
 
 export const RENDER_METADATA = '__renderTemplate__';
 export const HTTP_CODE_METADATA = '__httpCode__';
diff --git a/packages/common/exceptions/http.exception.ts b/packages/common/exceptions/http.exception.ts
index 800287695..7bdd1b111 100644
--- a/packages/common/exceptions/http.exception.ts
+++ b/packages/common/exceptions/http.exception.ts
@@ -99,9 +99,9 @@ export class HttpException extends Error {
     ) {
       this.message = (this.response as Record<string, any>).message;
     } else if (this.constructor) {
-      this.message =
-        this.constructor.name.match(/[A-Z][a-z]+|[0-9]+/g)?.join(' ') ??
-        'Error';
+      this.message = this.constructor.name
+        .match(/[A-Z][a-z]+|[0-9]+/g)
+        ?.join(' ') ?? 'Error';
     }
   }
 
diff --git a/packages/core/injector/compiler.ts b/packages/core/injector/compiler.ts
index 6c707fd61..353e27581 100644
--- a/packages/core/injector/compiler.ts
+++ b/packages/core/injector/compiler.ts
@@ -1,4 +1,8 @@
-import { DynamicModule, Type } from '@nestjs/common/interfaces';
+import {
+  DynamicModule,
+  ForwardReference,
+  Type,
+} from '@nestjs/common/interfaces';
 import { ModuleTokenFactory } from './module-token-factory';
 
 export interface ModuleFactory {
@@ -18,19 +22,25 @@ export class ModuleCompiler {
     return { type, dynamicMetadata, token };
   }
 
-  public extractMetadata(metatype: Type<any> | DynamicModule): {
+  public extractMetadata(
+    metatype: Type<any> | ForwardReference | DynamicModule,
+  ): {
     type: Type<any>;
     dynamicMetadata?: Partial<DynamicModule> | undefined;
   } {
     if (!this.isDynamicModule(metatype)) {
-      return { type: metatype };
+      return {
+        type: (metatype as ForwardReference)?.forwardRef
+          ? (metatype as ForwardReference).forwardRef()
+          : metatype,
+      };
     }
     const { module: type, ...dynamicMetadata } = metatype;
     return { type, dynamicMetadata };
   }
 
   public isDynamicModule(
-    module: Type<any> | DynamicModule,
+    module: Type<any> | DynamicModule | ForwardReference,
   ): module is DynamicModule {
     return !!(module as DynamicModule).module;
   }
diff --git a/packages/core/injector/injector.ts b/packages/core/injector/injector.ts
index 4930d4768..7d32a173f 100644
--- a/packages/core/injector/injector.ts
+++ b/packages/core/injector/injector.ts
@@ -353,9 +353,7 @@ export class Injector {
   }
 
   public reflectConstructorParams<T>(type: Type<T>): any[] {
-    const paramtypes = [
-      ...(Reflect.getMetadata(PARAMTYPES_METADATA, type) || []),
-    ];
+    const paramtypes = [...(Reflect.getMetadata(PARAMTYPES_METADATA, type) || [])];
     const selfParams = this.reflectSelfParams<T>(type);
 
     selfParams.forEach(({ index, param }) => (paramtypes[index] = param));
diff --git a/packages/core/inspector/uuid-factory.ts b/packages/core/inspector/uuid-factory.ts
index 979760975..62b973ff2 100644
--- a/packages/core/inspector/uuid-factory.ts
+++ b/packages/core/inspector/uuid-factory.ts
@@ -1,4 +1,4 @@
-import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
+import { randomStringGenerator } from '../../common/utils/random-string-generator.util';
 import { DeterministicUuidRegistry } from './deterministic-uuid-registry';
 
 export enum UuidFactoryMode {
diff --git a/packages/core/middleware/middleware-module.ts b/packages/core/middleware/middleware-module.ts
index a007c007c..ed736abb9 100644
--- a/packages/core/middleware/middleware-module.ts
+++ b/packages/core/middleware/middleware-module.ts
@@ -1,4 +1,4 @@
-import { HttpServer, Logger } from '@nestjs/common';
+import { HttpServer, Logger, VersioningType } from '@nestjs/common';
 import { RequestMethod } from '@nestjs/common/enums/request-method.enum';
 import {
   MiddlewareConfiguration,
@@ -6,7 +6,10 @@ import {
   RouteInfo,
 } from '@nestjs/common/interfaces/middleware';
 import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface';
-import { isUndefined } from '@nestjs/common/utils/shared.utils';
+import {
+  addLeadingSlash,
+  isUndefined,
+} from '@nestjs/common/utils/shared.utils';
 import { ApplicationConfig } from '../application-config';
 import { InvalidMiddlewareException } from '../errors/exceptions/invalid-middleware.exception';
 import { RuntimeException } from '../errors/exceptions/runtime.exception';
@@ -23,13 +26,13 @@ import {
   MiddlewareEntrypointMetadata,
 } from '../inspector/interfaces/entrypoint.interface';
 import { REQUEST_CONTEXT_ID } from '../router/request/request-constants';
+import { RoutePathFactory } from '../router/route-path-factory';
 import { RouterExceptionFilters } from '../router/router-exception-filters';
 import { RouterProxy } from '../router/router-proxy';
-import { isRequestMethodAll } from '../router/utils';
+import { isRequestMethodAll, isRouteExcluded } from '../router/utils';
 import { MiddlewareBuilder } from './builder';
 import { MiddlewareContainer } from './container';
 import { MiddlewareResolver } from './resolver';
-import { RouteInfoPathExtractor } from './route-info-path-extractor';
 import { RoutesMapper } from './routes-mapper';
 
 export class MiddlewareModule<
@@ -43,11 +46,13 @@ export class MiddlewareModule<
   private routerExceptionFilter: RouterExceptionFilters;
   private routesMapper: RoutesMapper;
   private resolver: MiddlewareResolver;
+  private config: ApplicationConfig;
   private container: NestContainer;
   private httpAdapter: HttpServer;
   private graphInspector: GraphInspector;
   private appOptions: TAppOptions;
-  private routeInfoPathExtractor: RouteInfoPathExtractor;
+
+  constructor(private readonly routePathFactory: RoutePathFactory) {}
 
   public async register(
     middlewareContainer: MiddlewareContainer,
@@ -68,7 +73,8 @@ export class MiddlewareModule<
     );
     this.routesMapper = new RoutesMapper(container);
     this.resolver = new MiddlewareResolver(middlewareContainer, injector);
-    this.routeInfoPathExtractor = new RouteInfoPathExtractor(config);
+
+    this.config = config;
     this.injector = injector;
     this.container = container;
     this.httpAdapter = httpAdapter;
@@ -301,18 +307,44 @@ export class MiddlewareModule<
 
   private async registerHandler(
     applicationRef: HttpServer,
-    routeInfo: RouteInfo,
+    { path, method, version }: RouteInfo,
     proxy: <TRequest, TResponse>(
       req: TRequest,
       res: TResponse,
       next: () => void,
     ) => void,
   ) {
-    const { method } = routeInfo;
-    const paths = this.routeInfoPathExtractor.extractPathsFrom(routeInfo);
+    const prefix = this.config.getGlobalPrefix();
+    const excludedRoutes = this.config.getGlobalPrefixOptions().exclude;
+    const isAWildcard = ['*', '/*', '(.*)', '/(.*)'].includes(path);
+    if (
+      (Array.isArray(excludedRoutes) &&
+        isRouteExcluded(excludedRoutes, path, method)) ||
+      isAWildcard
+    ) {
+      path = addLeadingSlash(path);
+    } else {
+      const basePath = addLeadingSlash(prefix);
+      if (basePath?.endsWith('/') && path?.startsWith('/')) {
+        // strip slash when a wildcard is being used
+        // and global prefix has been set
+        path = path?.slice(1);
+      }
+      path = basePath + path;
+    }
+
+    const applicationVersioningConfig = this.config.getVersioning();
+    if (version && applicationVersioningConfig.type === VersioningType.URI) {
+      const versionPrefix = this.routePathFactory.getVersionPrefix(
+        applicationVersioningConfig,
+      );
+      path = `/${versionPrefix}${version.toString()}${path}`;
+    }
+
     const isMethodAll = isRequestMethodAll(method);
     const requestMethod = RequestMethod[method];
     const router = await applicationRef.createMiddlewareFactory(method);
+
     const middlewareFunction = isMethodAll
       ? proxy
       : <TRequest, TResponse>(
@@ -325,7 +357,8 @@ export class MiddlewareModule<
           }
           return next();
         };
-    paths.forEach(path => router(path, middlewareFunction));
+
+    router(path, middlewareFunction);
   }
 
   private getContextId(request: unknown, isTreeDurable: boolean): ContextId {
diff --git a/packages/core/middleware/route-info-path-extractor.ts b/packages/core/middleware/route-info-path-extractor.ts
deleted file mode 100644
index dc03893ea..000000000
--- a/packages/core/middleware/route-info-path-extractor.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { VersioningType } from '@nestjs/common';
-import { RouteInfo } from '@nestjs/common/interfaces';
-import {
-  addLeadingSlash,
-  stripEndSlash,
-} from '@nestjs/common/utils/shared.utils';
-import { ApplicationConfig } from '../application-config';
-import { isRouteExcluded } from '../router/utils';
-import { RoutePathFactory } from './../router/route-path-factory';
-
-export class RouteInfoPathExtractor {
-  private routePathFactory: RoutePathFactory;
-
-  constructor(private readonly applicationConfig: ApplicationConfig) {
-    this.routePathFactory = new RoutePathFactory(applicationConfig);
-  }
-
-  public extractPathsFrom({ path, method, version }: RouteInfo) {
-    const prefixPath = stripEndSlash(
-      addLeadingSlash(this.applicationConfig.getGlobalPrefix()),
-    );
-    const excludedRoutes =
-      this.applicationConfig.getGlobalPrefixOptions().exclude;
-
-    const applicationVersioningConfig = this.applicationConfig.getVersioning();
-    let versionPath = '';
-    if (version && applicationVersioningConfig?.type === VersioningType.URI) {
-      const versionPrefix = this.routePathFactory.getVersionPrefix(
-        applicationVersioningConfig,
-      );
-      versionPath = addLeadingSlash(versionPrefix + version.toString());
-    }
-
-    const isAWildcard = ['*', '/*', '/*/', '(.*)', '/(.*)'].includes(path);
-    if (isAWildcard) {
-      return Array.isArray(excludedRoutes)
-        ? [
-            prefixPath + versionPath + addLeadingSlash(path),
-            ...excludedRoutes.map(
-              route => versionPath + addLeadingSlash(route.path),
-            ),
-          ]
-        : [prefixPath + versionPath + addLeadingSlash(path)];
-    }
-
-    if (
-      Array.isArray(excludedRoutes) &&
-      isRouteExcluded(excludedRoutes, path, method)
-    ) {
-      return [versionPath + addLeadingSlash(path)];
-    }
-
-    return [prefixPath + versionPath + addLeadingSlash(path)];
-  }
-}
diff --git a/packages/core/middleware/utils.ts b/packages/core/middleware/utils.ts
index d9365e90f..d0aad4849 100644
--- a/packages/core/middleware/utils.ts
+++ b/packages/core/middleware/utils.ts
@@ -1,10 +1,6 @@
 import { RequestMethod } from '@nestjs/common';
 import { HttpServer, RouteInfo, Type } from '@nestjs/common/interfaces';
-import {
-  addLeadingSlash,
-  isFunction,
-  isString,
-} from '@nestjs/common/utils/shared.utils';
+import { isFunction } from '@nestjs/common/utils/shared.utils';
 import { iterate } from 'iterare';
 import * as pathToRegexp from 'path-to-regexp';
 import { v4 as uuid } from 'uuid';
@@ -12,22 +8,13 @@ import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadat
 import { isRouteExcluded } from '../router/utils';
 
 export const mapToExcludeRoute = (
-  routes: (string | RouteInfo)[],
+  routes: RouteInfo[],
 ): ExcludeRouteMetadata[] => {
-  return routes.map(route => {
-    if (isString(route)) {
-      return {
-        path: route,
-        requestMethod: RequestMethod.ALL,
-        pathRegex: pathToRegexp(addLeadingSlash(route)),
-      };
-    }
-    return {
-      path: route.path,
-      requestMethod: route.method,
-      pathRegex: pathToRegexp(addLeadingSlash(route.path)),
-    };
-  });
+  return routes.map(({ path, method }) => ({
+    pathRegex: pathToRegexp(path),
+    requestMethod: method,
+    path,
+  }));
 };
 
 export const filterMiddleware = <T extends Function | Type<any> = any>(
diff --git a/packages/core/nest-application.ts b/packages/core/nest-application.ts
index 3b25d5877..b5fae0e22 100644
--- a/packages/core/nest-application.ts
+++ b/packages/core/nest-application.ts
@@ -7,6 +7,7 @@ import {
   NestHybridApplicationOptions,
   NestInterceptor,
   PipeTransform,
+  RequestMethod,
   VersioningOptions,
   VersioningType,
   WebSocketAdapter,
@@ -14,6 +15,7 @@ import {
 import {
   GlobalPrefixOptions,
   NestApplicationOptions,
+  RouteInfo,
 } from '@nestjs/common/interfaces';
 import {
   CorsOptions,
@@ -29,6 +31,7 @@ import {
 } from '@nestjs/common/utils/shared.utils';
 import { iterate } from 'iterare';
 import { platform } from 'os';
+import * as pathToRegexp from 'path-to-regexp';
 import { AbstractHttpAdapter } from './adapters';
 import { ApplicationConfig } from './application-config';
 import { MESSAGES } from './constants';
@@ -38,9 +41,10 @@ import { Injector } from './injector/injector';
 import { GraphInspector } from './inspector/graph-inspector';
 import { MiddlewareContainer } from './middleware/container';
 import { MiddlewareModule } from './middleware/middleware-module';
-import { mapToExcludeRoute } from './middleware/utils';
 import { NestApplicationContext } from './nest-application-context';
+import { ExcludeRouteMetadata } from './router/interfaces/exclude-route-metadata.interface';
 import { Resolver } from './router/interfaces/resolver.interface';
+import { RoutePathFactory } from './router/route-path-factory';
 import { RoutesResolver } from './router/routes-resolver';
 
 const { SocketModule } = optionalRequire(
@@ -85,8 +89,9 @@ export class NestApplication
 
     this.selectContextModule();
     this.registerHttpServer();
+
     this.injector = new Injector({ preview: this.appOptions.preview });
-    this.middlewareModule = new MiddlewareModule();
+    this.middlewareModule = new MiddlewareModule(new RoutePathFactory(config));
     this.routesResolver = new RoutesResolver(
       this.container,
       this.config,
@@ -367,9 +372,22 @@ export class NestApplication
   public setGlobalPrefix(prefix: string, options?: GlobalPrefixOptions): this {
     this.config.setGlobalPrefix(prefix);
     if (options) {
-      const exclude = options?.exclude
-        ? mapToExcludeRoute(options.exclude)
-        : [];
+      const exclude = options?.exclude.map(
+        (route: string | RouteInfo): ExcludeRouteMetadata => {
+          if (isString(route)) {
+            return {
+              requestMethod: RequestMethod.ALL,
+              pathRegex: pathToRegexp(addLeadingSlash(route)),
+              path: route,
+            };
+          }
+          return {
+            requestMethod: route.method,
+            pathRegex: pathToRegexp(addLeadingSlash(route.path)),
+            path: route.path,
+          };
+        },
+      );
       this.config.setGlobalPrefixOptions({
         ...options,
         exclude,
diff --git a/packages/core/router/interfaces/exclude-route-metadata.interface.ts b/packages/core/router/interfaces/exclude-route-metadata.interface.ts
index b7f93b600..dfa143041 100644
--- a/packages/core/router/interfaces/exclude-route-metadata.interface.ts
+++ b/packages/core/router/interfaces/exclude-route-metadata.interface.ts
@@ -1,11 +1,7 @@
 import { RequestMethod } from '@nestjs/common';
 
 export interface ExcludeRouteMetadata {
-  /**
-   * Route path.
-   */
   path: string;
-
   /**
    * Regular expression representing the route path.
    */
diff --git a/packages/core/router/utils/exclude-route.util.ts b/packages/core/router/utils/exclude-route.util.ts
index 8670f18f4..798260bf2 100644
--- a/packages/core/router/utils/exclude-route.util.ts
+++ b/packages/core/router/utils/exclude-route.util.ts
@@ -1,5 +1,4 @@
 import { RequestMethod } from '@nestjs/common';
-import { addLeadingSlash } from '@nestjs/common/utils/shared.utils';
 import { ExcludeRouteMetadata } from '../interfaces/exclude-route-metadata.interface';
 
 export const isRequestMethodAll = (method: RequestMethod) => {
@@ -16,7 +15,7 @@ export function isRouteExcluded(
       isRequestMethodAll(route.requestMethod) ||
       route.requestMethod === requestMethod
     ) {
-      return route.pathRegex.exec(addLeadingSlash(path));
+      return route.pathRegex.exec(path);
     }
     return false;
   });
diff --git a/packages/microservices/client/client-kafka.ts b/packages/microservices/client/client-kafka.ts
index 2932f1b13..ba9ad099a 100644
--- a/packages/microservices/client/client-kafka.ts
+++ b/packages/microservices/client/client-kafka.ts
@@ -42,13 +42,13 @@ let kafkaPackage: any = {};
 
 export class ClientKafka extends ClientProxy {
   protected logger = new Logger(ClientKafka.name);
-  protected client: Kafka | null = null;
-  protected consumer: Consumer | null = null;
-  protected producer: Producer | null = null;
-  protected parser: KafkaParser | null = null;
-  protected initialized: Promise<void> | null = null;
+  protected client: Kafka = null;
+  protected consumer: Consumer = null;
+  protected producer: Producer = null;
+  protected parser: KafkaParser = null;
   protected responsePatterns: string[] = [];
   protected consumerAssignments: { [key: string]: number } = {};
+
   protected brokers: string[] | BrokersFunction;
   protected clientId: string;
   protected groupId: string;
@@ -94,56 +94,46 @@ export class ClientKafka extends ClientProxy {
     this.consumer && (await this.consumer.disconnect());
     this.producer = null;
     this.consumer = null;
-    this.initialized = null;
     this.client = null;
   }
 
   public async connect(): Promise<Producer> {
-    if (this.initialized) {
-      return this.initialized.then(() => this.producer);
+    if (this.client) {
+      return this.producer;
     }
-    this.initialized = new Promise(async (resolve, reject) => {
-      try {
-        this.client = this.createClient();
-
-        if (!this.producerOnlyMode) {
-          const partitionAssigners = [
-            (
-              config: ConstructorParameters<
-                typeof KafkaReplyPartitionAssigner
-              >[1],
-            ) => new KafkaReplyPartitionAssigner(this, config),
-          ];
-
-          const consumerOptions = Object.assign(
-            {
-              partitionAssigners,
-            },
-            this.options.consumer || {},
-            {
-              groupId: this.groupId,
-            },
-          );
+    this.client = this.createClient();
+
+    if (!this.producerOnlyMode) {
+      const partitionAssigners = [
+        (
+          config: ConstructorParameters<typeof KafkaReplyPartitionAssigner>[1],
+        ) => new KafkaReplyPartitionAssigner(this, config),
+      ] as any[];
+
+      const consumerOptions = Object.assign(
+        {
+          partitionAssigners,
+        },
+        this.options.consumer || {},
+        {
+          groupId: this.groupId,
+        },
+      );
 
-          this.consumer = this.client.consumer(consumerOptions);
-          // set member assignments on join and rebalance
-          this.consumer.on(
-            this.consumer.events.GROUP_JOIN,
-            this.setConsumerAssignments.bind(this),
-          );
-          await this.consumer.connect();
-          await this.bindTopics();
-        }
+      this.consumer = this.client.consumer(consumerOptions);
+      // set member assignments on join and rebalance
+      this.consumer.on(
+        this.consumer.events.GROUP_JOIN,
+        this.setConsumerAssignments.bind(this),
+      );
+      await this.consumer.connect();
+      await this.bindTopics();
+    }
 
-        this.producer = this.client.producer(this.options.producer || {});
-        await this.producer.connect();
+    this.producer = this.client.producer(this.options.producer || {});
+    await this.producer.connect();
 
-        resolve();
-      } catch (err) {
-        reject(err);
-      }
-    });
-    return this.initialized.then(() => this.producer);
+    return this.producer;
   }
 
   public async bindTopics(): Promise<void> {


Test patch:
diff --git a/integration/hello-world/e2e/middleware-with-versioning.spec.ts b/integration/hello-world/e2e/middleware-with-versioning.spec.ts
index a16645b68..c8d800a90 100644
--- a/integration/hello-world/e2e/middleware-with-versioning.spec.ts
+++ b/integration/hello-world/e2e/middleware-with-versioning.spec.ts
@@ -77,26 +77,6 @@ describe('Middleware', () => {
     });
   });
 
-  describe('when using default URI versioning with the global prefix', () => {
-    beforeEach(async () => {
-      app = await createAppWithVersioning(
-        {
-          type: VersioningType.URI,
-          defaultVersion: VERSION_NEUTRAL,
-        },
-        async (app: INestApplication) => {
-          app.setGlobalPrefix('api');
-        },
-      );
-    });
-
-    it(`forRoutes({ path: '/versioned', version: '1', method: RequestMethod.ALL })`, () => {
-      return request(app.getHttpServer())
-        .get('/api/v1/versioned')
-        .expect(200, VERSIONED_VALUE);
-    });
-  });
-
   describe('when using HEADER versioning', () => {
     beforeEach(async () => {
       app = await createAppWithVersioning({
@@ -153,7 +133,6 @@ describe('Middleware', () => {
 
 async function createAppWithVersioning(
   versioningOptions: VersioningOptions,
-  beforeInit?: (app: INestApplication) => Promise<void>,
 ): Promise<INestApplication> {
   const app = (
     await Test.createTestingModule({
@@ -162,9 +141,6 @@ async function createAppWithVersioning(
   ).createNestApplication();
 
   app.enableVersioning(versioningOptions);
-  if (beforeInit) {
-    await beforeInit(app);
-  }
   await app.init();
 
   return app;
diff --git a/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts b/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts
index 74f8e9ad6..131d2dd03 100644
--- a/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts
+++ b/integration/nest-application/global-prefix/e2e/global-prefix.spec.ts
@@ -119,17 +119,6 @@ describe('Global prefix', () => {
     await request(server).get('/api/v1/middleware/foo').expect(404);
   });
 
-  it(`should get the params in the global prefix`, async () => {
-    app.setGlobalPrefix('/api/:tenantId');
-
-    server = app.getHttpServer();
-    await app.init();
-
-    await request(server)
-      .get('/api/test/params')
-      .expect(200, { '0': 'params', tenantId: 'test' });
-  });
-
   afterEach(async () => {
     await app.close();
   });
diff --git a/packages/core/test/application-config.spec.ts b/packages/core/test/application-config.spec.ts
index cff91bb11..9bf573bb9 100644
--- a/packages/core/test/application-config.spec.ts
+++ b/packages/core/test/application-config.spec.ts
@@ -24,9 +24,9 @@ describe('ApplicationConfig', () => {
       const options: GlobalPrefixOptions<ExcludeRouteMetadata> = {
         exclude: [
           {
-            path: '/health',
             pathRegex: new RegExp(/health/),
             requestMethod: RequestMethod.GET,
+            path: 'health',
           },
         ],
       };
diff --git a/packages/core/test/middleware/middleware-module.spec.ts b/packages/core/test/middleware/middleware-module.spec.ts
index 8f113dee1..e30ba206a 100644
--- a/packages/core/test/middleware/middleware-module.spec.ts
+++ b/packages/core/test/middleware/middleware-module.spec.ts
@@ -1,5 +1,5 @@
 import { Injectable } from '@nestjs/common';
-import { RouteInfoPathExtractor } from '@nestjs/core/middleware/route-info-path-extractor';
+import { RoutePathFactory } from '@nestjs/core/router/route-path-factory';
 import * as chai from 'chai';
 import { expect } from 'chai';
 import * as chaiAsPromised from 'chai-as-promised';
@@ -47,21 +47,15 @@ describe('MiddlewareModule', () => {
   beforeEach(() => {
     const container = new NestContainer();
     const appConfig = new ApplicationConfig();
+
     graphInspector = new GraphInspector(container);
-    middlewareModule = new MiddlewareModule();
-    middlewareModule['routerExceptionFilter'] = new RouterExceptionFilters(
-      new NestContainer(),
-      appConfig,
-      new NoopHttpAdapter({}),
-    );
-    middlewareModule['routeInfoPathExtractor'] = new RouteInfoPathExtractor(
-      appConfig,
-    );
+    middlewareModule = new MiddlewareModule(new RoutePathFactory(appConfig));
     middlewareModule['routerExceptionFilter'] = new RouterExceptionFilters(
       container,
       appConfig,
       new NoopHttpAdapter({}),
     );
+    middlewareModule['config'] = appConfig;
     middlewareModule['graphInspector'] = graphInspector;
   });
 
diff --git a/packages/core/test/middleware/route-info-path-extractor.spec.ts b/packages/core/test/middleware/route-info-path-extractor.spec.ts
deleted file mode 100644
index 9ff9c82d8..000000000
--- a/packages/core/test/middleware/route-info-path-extractor.spec.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import { RequestMethod, VersioningType } from '@nestjs/common';
-import { ApplicationConfig } from '@nestjs/core';
-import { mapToExcludeRoute } from '@nestjs/core/middleware/utils';
-import { expect } from 'chai';
-import { RouteInfoPathExtractor } from './../../middleware/route-info-path-extractor';
-
-describe('RouteInfoPathExtractor', () => {
-  describe('getPaths', () => {
-    let appConfig: ApplicationConfig;
-    let routeInfoPathExtractor: RouteInfoPathExtractor;
-    beforeEach(() => {
-      appConfig = new ApplicationConfig();
-      appConfig.enableVersioning({
-        type: VersioningType.URI,
-      });
-      routeInfoPathExtractor = new RouteInfoPathExtractor(appConfig);
-    });
-
-    it(`should return correct paths`, () => {
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: '*',
-          method: RequestMethod.ALL,
-        }),
-      ).to.eql(['/*']);
-
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: '*',
-          method: RequestMethod.ALL,
-          version: '1',
-        }),
-      ).to.eql(['/v1/*']);
-    });
-
-    it(`should return correct paths when set global prefix`, () => {
-      appConfig.setGlobalPrefix('api');
-
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: '*',
-          method: RequestMethod.ALL,
-        }),
-      ).to.eql(['/api/*']);
-
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: '*',
-          method: RequestMethod.ALL,
-          version: '1',
-        }),
-      ).to.eql(['/api/v1/*']);
-    });
-
-    it(`should return correct paths when set global prefix and global prefix options`, () => {
-      appConfig.setGlobalPrefix('api');
-      appConfig.setGlobalPrefixOptions({
-        exclude: mapToExcludeRoute(['foo']),
-      });
-
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: '*',
-          method: RequestMethod.ALL,
-        }),
-      ).to.eql(['/api/*', '/foo']);
-
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: '*',
-          method: RequestMethod.ALL,
-          version: '1',
-        }),
-      ).to.eql(['/api/v1/*', '/v1/foo']);
-
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: 'foo',
-          method: RequestMethod.ALL,
-          version: '1',
-        }),
-      ).to.eql(['/v1/foo']);
-
-      expect(
-        routeInfoPathExtractor.extractPathsFrom({
-          path: 'bar',
-          method: RequestMethod.ALL,
-          version: '1',
-        }),
-      ).to.eql(['/api/v1/bar']);
-    });
-  });
-});
diff --git a/packages/core/test/middleware/utils.spec.ts b/packages/core/test/middleware/utils.spec.ts
index a1c91d77b..754878c22 100644
--- a/packages/core/test/middleware/utils.spec.ts
+++ b/packages/core/test/middleware/utils.spec.ts
@@ -1,5 +1,4 @@
 import { RequestMethod, Type } from '@nestjs/common';
-import { addLeadingSlash } from '@nestjs/common/utils/shared.utils';
 import { expect } from 'chai';
 import * as sinon from 'sinon';
 import {
@@ -11,7 +10,6 @@ import {
   mapToExcludeRoute,
 } from '../../middleware/utils';
 import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
-import * as pathToRegexp from 'path-to-regexp';
 
 describe('middleware utils', () => {
   const noopAdapter = new NoopHttpAdapter({});
@@ -19,27 +17,6 @@ describe('middleware utils', () => {
   class Test {}
   function fnMiddleware(req, res, next) {}
 
-  describe('mapToExcludeRoute', () => {
-    it('should return exclude route metadata', () => {
-      const stringRoute = 'foo';
-      const routeInfo = {
-        path: 'bar',
-        method: RequestMethod.GET,
-      };
-      expect(mapToExcludeRoute([stringRoute, routeInfo])).to.eql([
-        {
-          path: stringRoute,
-          requestMethod: RequestMethod.ALL,
-          pathRegex: pathToRegexp(addLeadingSlash(stringRoute)),
-        },
-        {
-          path: routeInfo.path,
-          requestMethod: routeInfo.method,
-          pathRegex: pathToRegexp(addLeadingSlash(routeInfo.path)),
-        },
-      ]);
-    });
-  });
   describe('filterMiddleware', () => {
     let middleware: any[];
     beforeEach(() => {
diff --git a/packages/core/test/nest-application.spec.ts b/packages/core/test/nest-application.spec.ts
index 7ae09fe53..9e9bf348e 100644
--- a/packages/core/test/nest-application.spec.ts
+++ b/packages/core/test/nest-application.spec.ts
@@ -1,10 +1,8 @@
-import { RequestMethod } from '@nestjs/common';
 import { expect } from 'chai';
 import { ApplicationConfig } from '../application-config';
 import { NestContainer } from '../injector/container';
 import { GraphInspector } from '../inspector/graph-inspector';
 import { NestApplication } from '../nest-application';
-import { mapToExcludeRoute } from './../middleware/utils';
 import { NoopHttpAdapter } from './utils/noop-adapter.spec';
 
 describe('NestApplication', () => {
@@ -56,24 +54,4 @@ describe('NestApplication', () => {
       ).to.equal(1);
     });
   });
-  describe('Global Prefix', () => {
-    it('should get correct global prefix options', () => {
-      const applicationConfig = new ApplicationConfig();
-      const container = new NestContainer(applicationConfig);
-      const instance = new NestApplication(
-        container,
-        new NoopHttpAdapter({}),
-        applicationConfig,
-        new GraphInspector(container),
-        {},
-      );
-      const excludeRoute = ['foo', { path: 'bar', method: RequestMethod.GET }];
-      instance.setGlobalPrefix('api', {
-        exclude: excludeRoute,
-      });
-      expect(applicationConfig.getGlobalPrefixOptions()).to.eql({
-        exclude: mapToExcludeRoute(excludeRoute),
-      });
-    });
-  });
 });
diff --git a/packages/core/test/router/route-path-factory.spec.ts b/packages/core/test/router/route-path-factory.spec.ts
index 4c110651d..4bf952295 100644
--- a/packages/core/test/router/route-path-factory.spec.ts
+++ b/packages/core/test/router/route-path-factory.spec.ts
@@ -247,9 +247,9 @@ describe('RoutePathFactory', () => {
           sinon.stub(applicationConfig, 'getGlobalPrefixOptions').returns({
             exclude: [
               {
-                path: '/random',
                 pathRegex: pathToRegexp('/random'),
                 requestMethod: RequestMethod.ALL,
+                path: '/random',
               },
             ],
           });
@@ -266,9 +266,9 @@ describe('RoutePathFactory', () => {
           sinon.stub(applicationConfig, 'getGlobalPrefixOptions').returns({
             exclude: [
               {
-                path: '/cats',
                 pathRegex: pathToRegexp('/cats'),
                 requestMethod: RequestMethod.ALL,
+                path: '/cats',
               },
             ],
           });
@@ -285,9 +285,9 @@ describe('RoutePathFactory', () => {
             sinon.stub(applicationConfig, 'getGlobalPrefixOptions').returns({
               exclude: [
                 {
-                  path: '/cats',
                   pathRegex: pathToRegexp('/cats'),
                   requestMethod: RequestMethod.GET,
+                  path: '/cats',
                 },
               ],
             });
diff --git a/packages/microservices/test/client/client-kafka.spec.ts b/packages/microservices/test/client/client-kafka.spec.ts
index 4bc9e6eca..39743da52 100644
--- a/packages/microservices/test/client/client-kafka.spec.ts
+++ b/packages/microservices/test/client/client-kafka.spec.ts
@@ -11,6 +11,7 @@ import {
 } from '../../external/kafka.interface';
 
 describe('ClientKafka', () => {
+  // static
   const topic = 'test.topic';
   const partition = 0;
   const replyTopic = 'test.topic.reply';
@@ -24,6 +25,7 @@ describe('ClientKafka', () => {
   const heartbeat = async () => {};
   const pause = () => () => {};
 
+  // message
   const message: KafkaMessage = {
     key: Buffer.from(key),
     offset,
@@ -33,6 +35,7 @@ describe('ClientKafka', () => {
     attributes,
   };
 
+  // deserialized message
   const deserializedMessage: any = {
     key,
     offset,
@@ -44,6 +47,7 @@ describe('ClientKafka', () => {
     partition,
   };
 
+  // payloads
   const payload: EachMessagePayload = {
     topic,
     partition,
@@ -148,6 +152,26 @@ describe('ClientKafka', () => {
     pause,
   };
 
+  const deserializedPayloadError: EachMessagePayload = {
+    topic,
+    partition,
+    message: Object.assign(
+      {
+        headers: {
+          [KafkaHeaders.CORRELATION_ID]: correlationId,
+          [KafkaHeaders.NEST_ERR]: NO_MESSAGE_HANDLER,
+        },
+      },
+      deserializedMessage,
+      {
+        size: 0,
+        value: null,
+      },
+    ),
+    heartbeat,
+    pause,
+  };
+
   let client: ClientKafka;
   let callback: sinon.SinonSpy;
   let connect: sinon.SinonSpy;
@@ -158,7 +182,7 @@ describe('ClientKafka', () => {
   let consumerStub: sinon.SinonStub;
   let producerStub: sinon.SinonStub;
   let createClientStub: sinon.SinonStub;
-  let kafkaClient: any;
+  let kafkaClient;
 
   beforeEach(() => {
     client = new ClientKafka({});
@@ -298,8 +322,7 @@ describe('ClientKafka', () => {
       });
 
       it('should expect the connection to be reused', async () => {
-        (client as any).initialized = Promise.resolve({});
-
+        (client as any).client = kafkaClient;
         await client.connect();
 
         expect(createClientStub.calledOnce).to.be.false;
@@ -345,8 +368,7 @@ describe('ClientKafka', () => {
       });
 
       it('should expect the connection to be reused', async () => {
-        (client as any).initialized = Promise.resolve({});
-
+        (client as any).client = kafkaClient;
         await client.connect();
 
         expect(createClientStub.calledOnce).to.be.false;