nice-grpc Server Reflection
nice-grpc-server-reflection is a package within the nice-grpc ecosystem that provides gRPC server reflection capabilities for Node.js servers built with nice-grpc. Currently at version 3.0.4, it enables external gRPC tools like `grpcurl`, Postman, or client libraries to dynamically discover and introspect the server's exposed services, methods, and their protobuf message definitions at runtime without requiring pre-compiled `.proto` files on the client side. The library integrates seamlessly with the `nice-grpc` server, accepting a pre-generated protobuf descriptor set (.bin file) and a list of fully-qualified service names. As part of the actively maintained `nice-grpc` monorepo, it receives frequent updates, leveraging nice-grpc's TypeScript-first design, modern Promises/Async Iterables API, and robust middleware support. Its primary differentiator is its tight integration and idiomatic usage within the `nice-grpc` framework, providing a clear path for enabling standard gRPC reflection in TypeScript-based Node.js gRPC services.
Common errors
-
Argument of type 'IServerReflectionService' is not assignable to parameter of type 'CompatServiceDefinition'.
cause Type incompatibility between `nice-grpc-server-reflection`'s service definition and the `nice-grpc` server's expected service definition, often due to differing versions of `nice-grpc`, `@grpc/grpc-js`, or specific `ts-proto` generation configurations.fixUpdate `nice-grpc` and `nice-grpc-server-reflection` to compatible versions. If `ts-proto` is used, ensure `outputServices=nice-grpc,outputServices=generic-definitions` and `esModuleInterop=true` are configured during proto generation. -
Could not load server reflection. Details: no such Type or Enum '...' in Type '...'
cause The provided `protoset.bin` file is either corrupted, incomplete, or incorrectly generated, often missing definitions for types referenced within the reflected services.fixVerify the `protoc` command used to generate `protoset.bin` includes `--descriptor_set_out=path/to/protoset.bin` and crucially, `--include_imports`. Ensure all `.proto` files, including any imported dependencies, are correctly passed to `protoc` in the compilation step. -
proto: file appears multiple times: "path/to/some_file.proto" running at ...
cause This error typically occurs during protobuf descriptor set generation when the `protoc` compiler encounters the same `.proto` file (or one it imports) multiple times, often due to overlapping `I` (include) paths or duplicate file arguments.fixReview your `protoc` command. Ensure `-I` flags point to the root directories where your `.proto` files reside without overlap, and avoid listing the same `.proto` file multiple times as input. Use a consistent directory structure for your protos.
Warnings
- breaking Type compatibility issues can arise between nice-grpc-server-reflection's IServerReflectionService and nice-grpc's expected ServiceDefinition (or CompatServiceDefinition) when using specific TypeScript versions, gRPC-js versions, or certain protobuf code generation configurations (e.g., older ts-proto options). This typically manifests as a TypeScript compilation error.
- gotcha Generating the protobuf descriptor set (`.bin` file) requires specific `protoc` flags. Incorrectly generating this file, such as omitting `--descriptor_set_out` or `--include_imports`, or failing to include all relevant `.proto` files, will lead to the reflection service not being able to expose correct API information or failing to load entirely.
- gotcha Exposing the gRPC reflection service on publicly accessible endpoints can be a security concern, as it allows anyone to discover the full API surface, including all services, methods, and message structures. This is analogous to exposing an OpenAPI document for a REST API.
- gotcha The list of fully-qualified service names passed to the `ServerReflection` constructor must precisely match the package and service names defined in your `.proto` files (e.g., 'your.package.YourService'). Mismatches will result in those services not being discoverable via reflection.
Install
-
npm install nice-grpc-server-reflection -
yarn add nice-grpc-server-reflection -
pnpm add nice-grpc-server-reflection
Imports
- ServerReflectionService
const { ServerReflectionService } = require('nice-grpc-server-reflection')import { ServerReflectionService } from 'nice-grpc-server-reflection' - ServerReflection
import ServerReflection from 'nice-grpc-server-reflection'
import { ServerReflection } from 'nice-grpc-server-reflection' - GrpcServer
import { Server as GrpcServer } from 'nice-grpc'
Quickstart
import { Server as GrpcServer, ServiceDefinition, UntypedServiceImplementation } from 'nice-grpc';
import { ServerReflectionService, ServerReflection } from 'nice-grpc-server-reflection';
import * as fs from 'fs';
import * as path from 'path';
// 1. Define your .proto file (e.g., proto/greeting.proto):
// syntax = "proto3";
// package greeting;
// service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} }
// message HelloRequest { string name = 1; }
// message HelloReply { string message = 1; }
// 2. Generate the descriptor set using protoc:
// `protoc --descriptor_set_out=./proto/bundle.bin --include_imports -I./proto ./proto/greeting.proto`
// 3. Define your gRPC service implementation
const greeterService: ServiceDefinition & UntypedServiceImplementation = {
path: '/greeting.Greeter/SayHello',
requestStream: false,
responseStream: false,
requestType: {} as any, // In a real app, these would be generated types
responseType: {} as any, // In a real app, these would be generated types
async handler(request: { name: string }) {
return { message: `Hello, ${request.name}!` };
}
};
const main = async () => {
const server = new GrpcServer();
// Load the generated protobuf descriptor set
const protoset = fs.readFileSync(path.join(__dirname, '../proto/bundle.bin'));
// Add your application service
server.add('greeting.Greeter', greeterService);
// Add the server reflection service
// Pass the protoset and a list of fully-qualified service names to expose
server.add(
ServerReflectionService,
ServerReflection(
protoset,
['greeting.Greeter']
)
);
const port = 50051;
await server.listen(`0.0.0.0:${port}`);
console.log(`nice-grpc server with reflection listening on port ${port}`);
// You can now use grpcurl to inspect and call services:
// `grpcurl -plaintext localhost:50051 list`
// `grpcurl -plaintext localhost:50051 describe greeting.Greeter`
// `grpcurl -plaintext -d '{"name": "World"}' localhost:50051 greeting.Greeter/SayHello`
};
main().catch(console.error);