Repository: nestjs/nest
Pull request #10530 (2022-11-07 17:22:55)

feat(microservices): add package definition option to server-grpc
PR #8465 added support for packageDefinition for `ClientGrpc`, this PR adds it to `ServerGrpc`.

- Export common `packageDefinition` logic to `getGrpcPackageDefinition` and used it in both gRPC Server & Client, for consistency
- This change means that `GrpcOptions['options']['protoPath']` is now optional - `getGrpcPackageDefinition` verifies that either `protoPath` or `PackageDefinition` exist
- Also, changed the error thrown from `ServerGrpc.loadProto`: it was Error, now `InvalidProtoDefinitionException`, as it is in `ClientGrpc`
- Add a `loadProto` test for `ServerGrpc`, similar to the one that exists for `ClientGrpc`

## 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
- [x] 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". -->
- [ ] Bugfix
- [x] 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?
Currently, it's impossible to create a gRPC microservice by providing a `packageDefinition`, which means statically generated code can't be used — users must provide `*.proto` files. This situation isn't suitable for all (e.g. if your organization holds a dedicated repo for Protobufs, rather than storing them in the nestjs-based API repo they're developing, which is quite common).

Issue Number: N/A


## What is the new behavior?
It's possible to create a gRPC microservice by providing `packageDefinition`.

This change is inspired by https://github.com/nestjs/nest/pull/8465, which allowed `packageDefinition` to be provided when creating a gRPC **client**. I think that the fact that the author kept `GrpcOptions['options']['protoPath']` as a required field is confusing and inconsistent - even when providing `packageDefinition`, one still needs to provide `protoPath` which will simply be ignored. In this PR I suggest that `protoPath` will be optional, and when initiating the gRPC server we'll make sure that either `protoPath` or `packageDefinition` are supplied. I'd like to hear your thoughts on that.


## Does this PR introduce a breaking change?
- [ ] Yes
- [ ] No
- [x] Kinda :)

I'm not sure if changing `protoPath` to optional is considered a breaking change... either way, the documentation should be updated as well, so users will know it's not `protoPath` that's required, but either `protoPath` or `packageDefinition`. The docs are in a separate repo, right? 


## Other information

I'm open for discussion and willing to move around code (for example, I'm not sure that the `getGrpcPackageDefinition` validation should happen in `loadProto`, maybe in the constructor?). 

Thanks! 


Patch:
diff --git a/integration/nest-application/sse/src/app.controller.ts b/integration/nest-application/sse/src/app.controller.ts
deleted file mode 100644
index 2e36fa3d3..000000000
--- a/integration/nest-application/sse/src/app.controller.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Controller, MessageEvent, Sse } from '@nestjs/common';
-import { interval, map, Observable } from 'rxjs';
-
-@Controller()
-export class AppController {
-  @Sse('sse')
-  sse(): Observable<MessageEvent> {
-    return interval(1000).pipe(
-      map(_ => ({ data: { hello: 'world' } } as MessageEvent)),
-    );
-  }
-}
diff --git a/integration/nest-application/sse/src/app.module.ts b/integration/nest-application/sse/src/app.module.ts
deleted file mode 100644
index 848d4aaa7..000000000
--- a/integration/nest-application/sse/src/app.module.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Module } from '@nestjs/common';
-import { AppController } from './app.controller';
-
-@Module({
-  controllers: [AppController],
-})
-export class AppModule {}
diff --git a/integration/nest-application/sse/tsconfig.json b/integration/nest-application/sse/tsconfig.json
deleted file mode 100644
index 261f9bff2..000000000
--- a/integration/nest-application/sse/tsconfig.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "compilerOptions": {
-    "module": "commonjs",
-    "declaration": false,
-    "noImplicitAny": false,
-    "removeComments": true,
-    "lib": ["dom"],
-    "noLib": false,
-    "emitDecoratorMetadata": true,
-    "experimentalDecorators": true,
-    "target": "es6",
-    "sourceMap": true,
-    "allowJs": true,
-    "outDir": "./dist"
-  },
-  "include": [
-    "src/**/*",
-    "e2e/**/*"
-  ],
-  "exclude": [
-    "node_modules"
-  ]
-}
diff --git a/lerna.json b/lerna.json
index 2a6998ba6..2c7e871fc 100644
--- a/lerna.json
+++ b/lerna.json
@@ -3,5 +3,5 @@
   "packages": [
     "packages/*"
   ],
-  "version": "9.2.0"
+  "version": "9.1.6"
 }
diff --git a/packages/common/Readme.md b/packages/common/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/common/Readme.md
+++ b/packages/common/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/common/interfaces/nest-application-options.interface.ts b/packages/common/interfaces/nest-application-options.interface.ts
index 5d4c89cbc..5c67cd9d2 100644
--- a/packages/common/interfaces/nest-application-options.interface.ts
+++ b/packages/common/interfaces/nest-application-options.interface.ts
@@ -25,9 +25,4 @@ export interface NestApplicationOptions extends NestApplicationContextOptions {
    * Whether to register the raw request body on the request. Use `req.rawBody`.
    */
   rawBody?: boolean;
-  /**
-   * Force close open HTTP connections. Useful if restarting your application hangs due to
-   * keep-alive connections in the HTTP adapter.
-   */
-  forceCloseConnections?: boolean;
 }
diff --git a/packages/common/package.json b/packages/common/package.json
index c114967ef..802f9ce46 100644
--- a/packages/common/package.json
+++ b/packages/common/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/common",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@common)",
   "author": "Kamil Mysliwiec",
   "homepage": "https://nestjs.com",
diff --git a/packages/core/Readme.md b/packages/core/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/core/Readme.md
+++ b/packages/core/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/core/package.json b/packages/core/package.json
index 928e5a8be..b6eee28b1 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/core",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@core)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
@@ -36,7 +36,7 @@
     "uuid": "9.0.0"
   },
   "devDependencies": {
-    "@nestjs/common": "9.2.0"
+    "@nestjs/common": "9.1.6"
   },
   "peerDependencies": {
     "@nestjs/common": "^9.0.0",
diff --git a/packages/core/router/router-execution-context.ts b/packages/core/router/router-execution-context.ts
index 9e4fa897b..a40caa1e4 100644
--- a/packages/core/router/router-execution-context.ts
+++ b/packages/core/router/router-execution-context.ts
@@ -454,7 +454,6 @@ export class RouterExecutionContext {
       result = await this.responseController.transformToResult(result);
       !isResponseHandled &&
         (await this.responseController.apply(result, res, httpStatusCode));
-      return res;
     };
   }
 
diff --git a/packages/microservices/Readme.md b/packages/microservices/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/microservices/Readme.md
+++ b/packages/microservices/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/microservices/client/client-grpc.ts b/packages/microservices/client/client-grpc.ts
index 3110ca0d4..373ab68e1 100644
--- a/packages/microservices/client/client-grpc.ts
+++ b/packages/microservices/client/client-grpc.ts
@@ -10,6 +10,7 @@ import { ClientGrpc, GrpcOptions } from '../interfaces';
 import { ClientProxy } from './client-proxy';
 import { GRPC_CANCELLED } from './constants';
 import { ChannelOptions } from '../external/grpc-options.interface';
+import { getGrpcPackageDefinition } from '../helpers';
 
 let grpcPackage: any = {};
 let grpcProtoLoaderPackage: any = {};
@@ -286,16 +287,11 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
 
   public loadProto(): any {
     try {
-      const file = this.getOptionsProp(this.options, 'protoPath');
-      const loader = this.getOptionsProp(this.options, 'loader');
-
-      const packageDefinition =
-        this.getOptionsProp(this.options, 'packageDefinition') ||
-        grpcProtoLoaderPackage.loadSync(file, loader);
-
-      const packageObject =
-        grpcPackage.loadPackageDefinition(packageDefinition);
-      return packageObject;
+      const packageDefinition = getGrpcPackageDefinition(
+        this.options,
+        grpcProtoLoaderPackage,
+      );
+      return grpcPackage.loadPackageDefinition(packageDefinition);
     } catch (err) {
       const invalidProtoError = new InvalidProtoDefinitionException(err.path);
       const message =
diff --git a/packages/microservices/errors/invalid-grpc-package-definition-missing-package-definition.exception.ts b/packages/microservices/errors/invalid-grpc-package-definition-missing-package-definition.exception.ts
new file mode 100644
index 000000000..40ce6854c
--- /dev/null
+++ b/packages/microservices/errors/invalid-grpc-package-definition-missing-package-definition.exception.ts
@@ -0,0 +1,7 @@
+import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
+
+export class InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException extends RuntimeException {
+  constructor() {
+    super('protoPath or packageDefinition must be defined');
+  }
+}
diff --git a/packages/microservices/errors/invalid-grpc-package-definition-mutex.exception.ts b/packages/microservices/errors/invalid-grpc-package-definition-mutex.exception.ts
new file mode 100644
index 000000000..8720b688a
--- /dev/null
+++ b/packages/microservices/errors/invalid-grpc-package-definition-mutex.exception.ts
@@ -0,0 +1,9 @@
+import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
+
+export class InvalidGrpcPackageDefinitionMutexException extends RuntimeException {
+  constructor() {
+    super(
+      'Both protoPath and packageDefinition were provided, but only one can be defined',
+    );
+  }
+}
diff --git a/packages/microservices/helpers/grpc-helpers.ts b/packages/microservices/helpers/grpc-helpers.ts
new file mode 100644
index 000000000..b1d6460a4
--- /dev/null
+++ b/packages/microservices/helpers/grpc-helpers.ts
@@ -0,0 +1,24 @@
+import { GrpcOptions } from '../interfaces';
+import { InvalidGrpcPackageDefinitionMutexException } from '../errors/invalid-grpc-package-definition-mutex.exception';
+import { InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException } from '../errors/invalid-grpc-package-definition-missing-package-definition.exception';
+
+export function getGrpcPackageDefinition(
+  options: GrpcOptions['options'],
+  grpcProtoLoaderPackage: any,
+) {
+  const file = options['protoPath'];
+  const packageDefinition = options['packageDefinition'];
+
+  if ([file, packageDefinition].every(x => x != undefined)) {
+    throw new InvalidGrpcPackageDefinitionMutexException();
+  }
+
+  if ([file, packageDefinition].every(x => x == undefined)) {
+    throw new InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException();
+  }
+
+  return (
+    packageDefinition ||
+    grpcProtoLoaderPackage.loadSync(file, options['loader'])
+  );
+}
diff --git a/packages/microservices/helpers/index.ts b/packages/microservices/helpers/index.ts
index ff6118b51..e395ce7dc 100644
--- a/packages/microservices/helpers/index.ts
+++ b/packages/microservices/helpers/index.ts
@@ -3,3 +3,4 @@ export * from './kafka-logger';
 export * from './kafka-parser';
 export * from './kafka-reply-partition-assigner';
 export * from './tcp-socket';
+export * from './grpc-helpers';
diff --git a/packages/microservices/interfaces/microservice-configuration.interface.ts b/packages/microservices/interfaces/microservice-configuration.interface.ts
index 11e5b414e..b16459a98 100644
--- a/packages/microservices/interfaces/microservice-configuration.interface.ts
+++ b/packages/microservices/interfaces/microservice-configuration.interface.ts
@@ -50,7 +50,7 @@ export interface GrpcOptions {
     };
     channelOptions?: ChannelOptions;
     credentials?: any;
-    protoPath: string | string[];
+    protoPath?: string | string[];
     package: string | string[];
     protoLoader?: string;
     packageDefinition?: any;
diff --git a/packages/microservices/package.json b/packages/microservices/package.json
index bd3cbd239..6c0bf0125 100644
--- a/packages/microservices/package.json
+++ b/packages/microservices/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/microservices",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@microservices)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
@@ -21,8 +21,8 @@
     "tslib": "2.4.1"
   },
   "devDependencies": {
-    "@nestjs/common": "9.2.0",
-    "@nestjs/core": "9.2.0"
+    "@nestjs/common": "9.1.6",
+    "@nestjs/core": "9.1.6"
   },
   "peerDependencies": {
     "@grpc/grpc-js": "*",
diff --git a/packages/microservices/server/server-grpc.ts b/packages/microservices/server/server-grpc.ts
index e001907a0..fa7e579d7 100644
--- a/packages/microservices/server/server-grpc.ts
+++ b/packages/microservices/server/server-grpc.ts
@@ -18,6 +18,7 @@ import { ChannelOptions } from '../external/grpc-options.interface';
 import { CustomTransportStrategy, MessageHandler } from '../interfaces';
 import { GrpcOptions } from '../interfaces/microservice-configuration.interface';
 import { Server } from './server';
+import { getGrpcPackageDefinition } from '../helpers';
 
 let grpcPackage: any = {};
 let grpcProtoLoaderPackage: any = {};
@@ -380,20 +381,18 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
 
   public loadProto(): any {
     try {
-      const file = this.getOptionsProp(this.options, 'protoPath');
-      const loader = this.getOptionsProp(this.options, 'loader');
-
-      const packageDefinition = grpcProtoLoaderPackage.loadSync(file, loader);
-      const packageObject =
-        grpcPackage.loadPackageDefinition(packageDefinition);
-      return packageObject;
+      const packageDefinition = getGrpcPackageDefinition(
+        this.options,
+        grpcProtoLoaderPackage,
+      );
+      return grpcPackage.loadPackageDefinition(packageDefinition);
     } catch (err) {
       const invalidProtoError = new InvalidProtoDefinitionException(err.path);
       const message =
         err && err.message ? err.message : invalidProtoError.message;
 
       this.logger.error(message, invalidProtoError.stack);
-      throw err;
+      throw invalidProtoError;
     }
   }
 
diff --git a/packages/platform-express/Readme.md b/packages/platform-express/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/platform-express/Readme.md
+++ b/packages/platform-express/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/platform-express/adapters/express-adapter.ts b/packages/platform-express/adapters/express-adapter.ts
index 9d02e8b1f..718a3f4fd 100644
--- a/packages/platform-express/adapters/express-adapter.ts
+++ b/packages/platform-express/adapters/express-adapter.ts
@@ -33,7 +33,7 @@ import * as cors from 'cors';
 import * as express from 'express';
 import * as http from 'http';
 import * as https from 'https';
-import { Duplex, pipeline } from 'stream';
+import { pipeline } from 'stream';
 import { ServeStaticOptions } from '../interfaces/serve-static-options.interface';
 import { getBodyParserOptions } from './utils/get-body-parser-options.util';
 
@@ -49,7 +49,6 @@ type VersionedRoute = <
 export class ExpressAdapter extends AbstractHttpAdapter {
   private readonly routerMethodFactory = new RouterMethodFactory();
   private readonly logger = new Logger(ExpressAdapter.name);
-  private readonly openConnections = new Set<Duplex>();
 
   constructor(instance?: any) {
     super(instance || express());
@@ -140,8 +139,6 @@ export class ExpressAdapter extends AbstractHttpAdapter {
   }
 
   public close() {
-    this.closeOpenConnections();
-
     if (!this.httpServer) {
       return undefined;
     }
@@ -210,13 +207,9 @@ export class ExpressAdapter extends AbstractHttpAdapter {
         options.httpsOptions,
         this.getInstance(),
       );
-    } else {
-      this.httpServer = http.createServer(this.getInstance());
-    }
-
-    if (options?.forceCloseConnections) {
-      this.trackOpenConnections();
+      return;
     }
+    this.httpServer = http.createServer(this.getInstance());
   }
 
   public registerParserMiddleware(prefix?: string, rawBody?: boolean) {
@@ -389,21 +382,6 @@ export class ExpressAdapter extends AbstractHttpAdapter {
     }
   }
 
-  private trackOpenConnections() {
-    this.httpServer.on('connection', (socket: Duplex) => {
-      this.openConnections.add(socket);
-
-      socket.on('close', () => this.openConnections.delete(socket));
-    });
-  }
-
-  private closeOpenConnections() {
-    for (const socket of this.openConnections) {
-      socket.destroy();
-      this.openConnections.delete(socket);
-    }
-  }
-
   private isMiddlewareApplied(name: string): boolean {
     const app = this.getInstance();
     return (
diff --git a/packages/platform-express/package.json b/packages/platform-express/package.json
index 65b0b6788..def9d485a 100644
--- a/packages/platform-express/package.json
+++ b/packages/platform-express/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/platform-express",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@platform-express)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
@@ -24,8 +24,8 @@
     "tslib": "2.4.1"
   },
   "devDependencies": {
-    "@nestjs/common": "9.2.0",
-    "@nestjs/core": "9.2.0"
+    "@nestjs/common": "9.1.6",
+    "@nestjs/core": "9.1.6"
   },
   "peerDependencies": {
     "@nestjs/common": "^9.0.0",
diff --git a/packages/platform-fastify/Readme.md b/packages/platform-fastify/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/platform-fastify/Readme.md
+++ b/packages/platform-fastify/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/platform-fastify/package.json b/packages/platform-fastify/package.json
index 5a8f47967..826546bd2 100644
--- a/packages/platform-fastify/package.json
+++ b/packages/platform-fastify/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/platform-fastify",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@platform-fastify)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
diff --git a/packages/platform-socket.io/Readme.md b/packages/platform-socket.io/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/platform-socket.io/Readme.md
+++ b/packages/platform-socket.io/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/platform-socket.io/package.json b/packages/platform-socket.io/package.json
index 057b5e2bf..7fbfb26f9 100644
--- a/packages/platform-socket.io/package.json
+++ b/packages/platform-socket.io/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/platform-socket.io",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@platform-socket.io)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
diff --git a/packages/platform-ws/Readme.md b/packages/platform-ws/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/platform-ws/Readme.md
+++ b/packages/platform-ws/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/platform-ws/package.json b/packages/platform-ws/package.json
index 495095fbc..cb5d2c0ce 100644
--- a/packages/platform-ws/package.json
+++ b/packages/platform-ws/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/platform-ws",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@platform-ws)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
diff --git a/packages/websockets/Readme.md b/packages/websockets/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/websockets/Readme.md
+++ b/packages/websockets/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/websockets/package.json b/packages/websockets/package.json
index e059154f2..9412b9cc7 100644
--- a/packages/websockets/package.json
+++ b/packages/websockets/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/websockets",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@websockets)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
@@ -17,8 +17,8 @@
     "tslib": "2.4.1"
   },
   "devDependencies": {
-    "@nestjs/common": "9.2.0",
-    "@nestjs/core": "9.2.0"
+    "@nestjs/common": "9.1.6",
+    "@nestjs/core": "9.1.6"
   },
   "peerDependencies": {
     "@nestjs/common": "^9.0.0",
diff --git a/sample/28-sse/src/main.ts b/sample/28-sse/src/main.ts
index 4d8008da5..e1e10d969 100644
--- a/sample/28-sse/src/main.ts
+++ b/sample/28-sse/src/main.ts
@@ -2,10 +2,7 @@ import { NestFactory } from '@nestjs/core';
 import { AppModule } from './app.module';
 
 async function bootstrap() {
-  const app = await NestFactory.create(AppModule, {
-    forceCloseConnections: true,
-  });
-  app.enableShutdownHooks();
+  const app = await NestFactory.create(AppModule);
   await app.listen(3000);
   console.log(`Application is running on: ${await app.getUrl()}`);
 }


Test patch:
diff --git a/integration/nest-application/sse/e2e/express.spec.ts b/integration/nest-application/sse/e2e/express.spec.ts
deleted file mode 100644
index 363dc807f..000000000
--- a/integration/nest-application/sse/e2e/express.spec.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { NestExpressApplication } from '@nestjs/platform-express';
-import { Test } from '@nestjs/testing';
-import { expect } from 'chai';
-import * as EventSource from 'eventsource';
-import { AppModule } from '../src/app.module';
-
-describe('Sse (Express Application)', () => {
-  let app: NestExpressApplication;
-  let eventSource: EventSource;
-
-  describe('without forceCloseConnections', () => {
-    beforeEach(async () => {
-      const moduleFixture = await Test.createTestingModule({
-        imports: [AppModule],
-      }).compile();
-
-      app = moduleFixture.createNestApplication<NestExpressApplication>();
-
-      await app.listen(3000);
-      const url = await app.getUrl();
-
-      eventSource = new EventSource(url + '/sse', {
-        headers: { connection: 'keep-alive' },
-      });
-    });
-
-    // The order of actions is very important here. When not using `forceCloseConnections`,
-    // the SSe eventsource should close the connections in order to signal the server that
-    // the keep-alive connection can be ended.
-    afterEach(async () => {
-      eventSource.close();
-
-      await app.close();
-    });
-
-    it('receives events from server', done => {
-      eventSource.addEventListener('message', event => {
-        expect(JSON.parse(event.data)).to.eql({
-          hello: 'world',
-        });
-        done();
-      });
-    });
-  });
-
-  describe('with forceCloseConnections', () => {
-    beforeEach(async () => {
-      const moduleFixture = await Test.createTestingModule({
-        imports: [AppModule],
-      }).compile();
-
-      app = moduleFixture.createNestApplication<NestExpressApplication>({
-        forceCloseConnections: true,
-      });
-
-      await app.listen(3000);
-      const url = await app.getUrl();
-
-      eventSource = new EventSource(url + '/sse', {
-        headers: { connection: 'keep-alive' },
-      });
-    });
-
-    afterEach(async () => {
-      await app.close();
-
-      eventSource.close();
-    });
-
-    it('receives events from server', done => {
-      eventSource.addEventListener('message', event => {
-        expect(JSON.parse(event.data)).to.eql({
-          hello: 'world',
-        });
-        done();
-      });
-    });
-  });
-});
diff --git a/integration/nest-application/sse/e2e/fastify.spec.ts b/integration/nest-application/sse/e2e/fastify.spec.ts
deleted file mode 100644
index 4fc1ebf20..000000000
--- a/integration/nest-application/sse/e2e/fastify.spec.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import {
-  FastifyAdapter,
-  NestFastifyApplication,
-} from '@nestjs/platform-fastify';
-import { Test } from '@nestjs/testing';
-import { expect } from 'chai';
-import * as EventSource from 'eventsource';
-import { AppModule } from '../src/app.module';
-
-describe('Sse (Fastify Application)', () => {
-  let app: NestFastifyApplication;
-  let eventSource: EventSource;
-
-  describe('without forceCloseConnections', () => {
-    beforeEach(async () => {
-      const moduleFixture = await Test.createTestingModule({
-        imports: [AppModule],
-      }).compile();
-
-      app = moduleFixture.createNestApplication<NestFastifyApplication>(
-        new FastifyAdapter(),
-      );
-
-      await app.listen(3000);
-      const url = await app.getUrl();
-
-      eventSource = new EventSource(url + '/sse', {
-        headers: { connection: 'keep-alive' },
-      });
-    });
-
-    // The order of actions is very important here. When not using `forceCloseConnections`,
-    // the SSe eventsource should close the connections in order to signal the server that
-    // the keep-alive connection can be ended.
-    afterEach(async () => {
-      eventSource.close();
-
-      await app.close();
-    });
-
-    it('receives events from server', done => {
-      eventSource.addEventListener('message', event => {
-        expect(JSON.parse(event.data)).to.eql({
-          hello: 'world',
-        });
-        done();
-      });
-    });
-  });
-
-  describe('with forceCloseConnections', () => {
-    beforeEach(async () => {
-      const moduleFixture = await Test.createTestingModule({
-        imports: [AppModule],
-      }).compile();
-
-      app = moduleFixture.createNestApplication<NestFastifyApplication>(
-        new FastifyAdapter({
-          forceCloseConnections: true,
-        }),
-      );
-
-      await app.listen(3000);
-      const url = await app.getUrl();
-
-      eventSource = new EventSource(url + '/sse', {
-        headers: { connection: 'keep-alive' },
-      });
-    });
-
-    afterEach(async () => {
-      await app.close();
-
-      eventSource.close();
-    });
-
-    it('receives events from server', done => {
-      eventSource.addEventListener('message', event => {
-        expect(JSON.parse(event.data)).to.eql({
-          hello: 'world',
-        });
-        done();
-      });
-    });
-  });
-});
diff --git a/packages/microservices/test/client/client-grpc.spec.ts b/packages/microservices/test/client/client-grpc.spec.ts
index 6d38e5941..9e5375dbd 100644
--- a/packages/microservices/test/client/client-grpc.spec.ts
+++ b/packages/microservices/test/client/client-grpc.spec.ts
@@ -3,10 +3,11 @@ import { expect } from 'chai';
 import { join } from 'path';
 import { Observable, Subject } from 'rxjs';
 import * as sinon from 'sinon';
-import { ClientGrpcProxy } from '../../client/client-grpc';
+import { ClientGrpcProxy } from '../../client';
 import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception';
 import { InvalidGrpcServiceException } from '../../errors/invalid-grpc-service.exception';
 import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception';
+import * as grpcHelpers from '../../helpers/grpc-helpers';
 
 class NoopLogger extends Logger {
   log(message: any, context?: string): void {}
@@ -386,13 +387,18 @@ describe('ClientGrpcProxy', () => {
   describe('loadProto', () => {
     describe('when proto is invalid', () => {
       it('should throw InvalidProtoDefinitionException', () => {
-        sinon.stub(client, 'getOptionsProp' as any).callsFake(() => {
+        const getPackageDefinitionStub = sinon.stub(
+          grpcHelpers,
+          'getGrpcPackageDefinition' as any,
+        );
+        getPackageDefinitionStub.callsFake(() => {
           throw new Error();
         });
         (client as any).logger = new NoopLogger();
         expect(() => client.loadProto()).to.throws(
           InvalidProtoDefinitionException,
         );
+        getPackageDefinitionStub.restore();
       });
     });
   });
diff --git a/packages/microservices/test/helpers/grpc-helpers.spec.ts b/packages/microservices/test/helpers/grpc-helpers.spec.ts
new file mode 100644
index 000000000..4348ecb59
--- /dev/null
+++ b/packages/microservices/test/helpers/grpc-helpers.spec.ts
@@ -0,0 +1,56 @@
+import { expect } from 'chai';
+import { getGrpcPackageDefinition } from '../../helpers/grpc-helpers';
+import { InvalidGrpcPackageDefinitionMutexException } from '../../errors/invalid-grpc-package-definition-mutex.exception';
+import { InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException } from '../../errors/invalid-grpc-package-definition-missing-package-definition.exception';
+
+const grpcProtoLoaderPackage = { loadSync: (a, b) => 'withLoader' };
+
+describe('getGrpcPackageDefinition', () => {
+  it('missing both protoPath and packageDefinition', () => {
+    expect(() =>
+      getGrpcPackageDefinition(
+        {
+          package: 'somePackage',
+        },
+        grpcProtoLoaderPackage,
+      ),
+    ).to.throw(InvalidGrpcPackageDefinitionMissingPacakgeDefinitionException);
+  });
+
+  it('got both protoPath and packageDefinition', () => {
+    expect(() =>
+      getGrpcPackageDefinition(
+        {
+          package: 'somePackage',
+          protoPath: 'some/path',
+          packageDefinition: {},
+        },
+        grpcProtoLoaderPackage,
+      ),
+    ).to.throw(InvalidGrpcPackageDefinitionMutexException);
+  });
+
+  it('success with protoPath', () => {
+    expect(() =>
+      getGrpcPackageDefinition(
+        {
+          package: 'somePackage',
+          protoPath: 'some/path',
+        },
+        grpcProtoLoaderPackage,
+      ),
+    ).to.not.throw(Error);
+  });
+
+  it('success with packageDef', () => {
+    expect(() =>
+      getGrpcPackageDefinition(
+        {
+          package: 'somePackage',
+          packageDefinition: {},
+        },
+        grpcProtoLoaderPackage,
+      ),
+    ).to.not.throw(Error);
+  });
+});
diff --git a/packages/microservices/test/server/server-grpc.spec.ts b/packages/microservices/test/server/server-grpc.spec.ts
index 8f9f8dd6f..073d74991 100644
--- a/packages/microservices/test/server/server-grpc.spec.ts
+++ b/packages/microservices/test/server/server-grpc.spec.ts
@@ -6,7 +6,9 @@ import * as sinon from 'sinon';
 import { CANCEL_EVENT } from '../../constants';
 import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception';
 import { GrpcMethodStreamingType } from '../../index';
-import { ServerGrpc } from '../../server/server-grpc';
+import { ServerGrpc } from '../../server';
+import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception';
+import * as grpcHelpers from '../../helpers/grpc-helpers';
 
 class NoopLogger extends Logger {
   log(message: any, context?: string): void {}
@@ -532,6 +534,25 @@ describe('ServerGrpc', () => {
     });
   });
 
+  describe('loadProto', () => {
+    describe('when proto is invalid', () => {
+      it('should throw InvalidProtoDefinitionException', () => {
+        const getPackageDefinitionStub = sinon.stub(
+          grpcHelpers,
+          'getGrpcPackageDefinition' as any,
+        );
+        getPackageDefinitionStub.callsFake(() => {
+          throw new Error();
+        });
+        (server as any).logger = new NoopLogger();
+        expect(() => server.loadProto()).to.throws(
+          InvalidProtoDefinitionException,
+        );
+        getPackageDefinitionStub.restore();
+      });
+    });
+  });
+
   describe('close', () => {
     it('should call "forceShutdown"', () => {
       const grpcClient = { forceShutdown: sinon.spy() };
diff --git a/packages/testing/Readme.md b/packages/testing/Readme.md
index 5203ac4c2..02b630f45 100644
--- a/packages/testing/Readme.md
+++ b/packages/testing/Readme.md
@@ -90,9 +90,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
 <a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
 <td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
 <td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
-<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
-<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td></tr>
-</table>
+<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td></tr></table>
 
 #### Sponsors
 
diff --git a/packages/testing/package.json b/packages/testing/package.json
index 2733cf44a..4179cd4b3 100644
--- a/packages/testing/package.json
+++ b/packages/testing/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/testing",
-  "version": "9.2.0",
+  "version": "9.1.6",
   "description": "Nest - modern, fast, powerful node.js web framework (@testing)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",