gRPC Server Reflection for Node.js
grpc-server-reflection is a Node.js library that implements the gRPC server reflection protocol, allowing gRPC clients to discover services and methods exposed by a server at runtime without prior knowledge of its `.proto` definitions. Currently at version 0.1.5, it provides robust reflection capabilities by leveraging pre-generated binary descriptor sets (via `grpc_tools_node_protoc`) rather than relying on dynamic schema parsing or problematic `protobuf-js` binary formats. It is designed to be framework-agnostic and offers full support for both the modern `@grpc/grpc-js` library and the older `grpc` package. Key differentiators include its comprehensive service detection, full feature set compared to alternatives, and its ability to handle complex `proto` definitions via a static descriptor file. While the release cadence is not explicitly defined, given its pre-1.0 version, users can expect incremental updates for bug fixes and stability improvements.
Common errors
-
Error: Failed to load descriptor set from path/to/descriptor_set.bin
cause The binary descriptor set file specified in `addReflection` does not exist or is not accessible.fixVerify that `path/to/descriptor_set.bin` is the correct and absolute path to your generated descriptor set file and that the Node.js process has read permissions for it. Ensure the file was generated by `grpc_tools_node_protoc`. -
gRPC reflection client fails to discover methods/services, or reports 'No such method' errors.
cause The descriptor set was generated without `--include_imports`, leading to an incomplete view of the service definitions, especially for services that import other proto files.fixRe-run `grpc_tools_node_protoc` to generate your `descriptor_set.bin` file, explicitly adding the `--include_imports` flag, e.g., `grpc_tools_node_protoc --descriptor_set_out=output.bin --include_imports your_protos/**/*.proto`. -
TypeError: addReflection is not a function
cause This typically occurs when mixing CommonJS `require()` with an ESM-only package or when using incorrect destructuring with `require()`.fixFor ESM projects, use `import { addReflection } from 'grpc-server-reflection'`. For CommonJS, if the package explicitly supports it, use `const { addReflection } = require('grpc-server-reflection');`. Ensure your `package.json` `type` field is correctly set to `module` for ESM, or use a bundler that handles CJS/ESM interop.
Warnings
- gotcha The `grpc_tools_node_protoc` command used to generate the descriptor set *must* include the `--include_imports` flag. Omitting this flag will result in an incomplete descriptor set, causing clients to fail when trying to reflect imported types or services.
- gotcha The server reflection service itself (usually `grpc.reflection.v1alpha.ServerReflection`) is not automatically included in the descriptor set generated by `protoc` unless explicitly defined within one of your `.proto` files and included in the compilation. This means clients cannot reflect upon the reflection service itself.
- gotcha This package relies on a pre-generated binary descriptor set file (e.g., `descriptor_set.bin`). It does not dynamically parse `.proto` files at runtime. Users must ensure this file is correctly generated and available to the server.
- gotcha Being a pre-1.0.0 package (current version 0.1.5), its API might not be entirely stable, and future minor versions could introduce breaking changes or significant modifications. Exercise caution when upgrading and review release notes.
Install
-
npm install grpc-server-reflection -
yarn add grpc-server-reflection -
pnpm add grpc-server-reflection
Imports
- addReflection
const { addReflection } = require('grpc-server-reflection')import { addReflection } from 'grpc-server-reflection' - Server
const { Server } = require('@grpc/grpc-js');import * as grpc from '@grpc/grpc-js'; const server = new grpc.Server()
- ServerUnaryCall
const ServerUnaryCall = require('@grpc/grpc-js').ServerUnaryCallimport { ServerUnaryCall } from '@grpc/grpc-js'
Quickstart
import * as grpc from '@grpc/grpc-js';
import { addReflection } from 'grpc-server-reflection';
import * as path from 'path';
import * as fs from 'fs';
// --- Placeholder for your proto definition ---
// syntax = "proto3";
// package myapp;
// service Greeter {
// rpc SayHello (HelloRequest) returns (HelloReply) {}
// }
// message HelloRequest { string name = 1; }
// message HelloReply { string message = 1; }
// ------------------------------------------
// Dummy service implementation
const greeterService = {
SayHello: (call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
const name = call.request.name || 'World';
callback(null, { message: `Hello, ${name}!` });
},
};
// Resolve paths for descriptor set
const DESCRIPTOR_SET_PATH = path.resolve(__dirname, 'descriptor_set.bin');
// In a real application, you must generate descriptor_set.bin using grpc_tools_node_protoc:
// grpc_tools_node_protoc --descriptor_set_out=${DESCRIPTOR_SET_PATH} --include_imports your_protos/**/*.proto
if (!fs.existsSync(DESCRIPTOR_SET_PATH)) {
console.warn(`
WARNING: Descriptor set file not found at ${DESCRIPTOR_SET_PATH}.
Reflection will not function without it. Please generate it.
Example command:
grpc_tools_node_protoc --descriptor_set_out=${DESCRIPTOR_SET_PATH} --include_imports --proto_path=./path/to/protos ./path/to/protos/*.proto
`);
// For quickstart to be runnable without actual proto generation, create a dummy file.
// In production, this file *must* be correctly generated.
fs.writeFileSync(DESCRIPTOR_SET_PATH, Buffer.from('DUMMY_DESCRIPTOR_SET'));
}
const server = new grpc.Server();
// Register your actual gRPC services
server.addService({
// This is a minimal representation; real services use generated types.
SayHello: {
path: '/myapp.Greeter/SayHello',
requestStream: false,
responseStream: false,
requestSerialize: (value: any) => Buffer.from(JSON.stringify(value)),
requestDeserialize: (buffer: Buffer) => JSON.parse(buffer.toString()),
responseSerialize: (value: any) => Buffer.from(JSON.stringify(value)),
responseDeserialize: (buffer: Buffer) => JSON.parse(buffer.toString()),
},
}, greeterService);
// Add gRPC server reflection to the server instance
addReflection(server, DESCRIPTOR_SET_PATH);
const port = '0.0.0.0:50051';
server.bindAsync(port, grpc.ServerCredentials.createInsecure(), (err, boundPort) => {
if (err) {
console.error(`Server bind failed on ${port}: ${err.message}`);
} else {
server.start();
console.log(`gRPC server listening on ${boundPort} with reflection enabled.`);
}
});