npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

grpc-descriptor-reflection

v1.0.0

Published

gRPC reflection service for @grpc/grpc-js that serves raw FileDescriptorProto bytes from a pre-built descriptor set, preserving all extensions and custom options (google.api.http, openapiv2, etc.)

Readme

grpc-descriptor-reflection

A gRPC server reflection service for @grpc/grpc-js that serves raw FileDescriptorProto bytes from a pre-built descriptor set, preserving all extensions and custom options byte-for-byte.

Why?

Existing reflection libraries for Node.js gRPC lose extension fields during serialization:

  • @grpc/reflection decodes descriptors through protobufjs and re-encodes them. protobufjs silently drops all extension fields (google.api.http, openapiv2_operation, custom options, etc.) because they are not declared fields on the standard option message types.

  • grpc-server-reflection uses google-protobuf which performs a similar decode/re-encode round-trip, with no guarantee of byte-for-byte fidelity.

This means any downstream consumer relying on reflection (API gateways, documentation generators, gRPC-to-HTTP transcoders) will see methods without HTTP bindings, missing OpenAPI annotations, or stripped custom options.

grpc-descriptor-reflection solves this by:

  1. Parsing the FileDescriptorSet wire format directly to extract each FileDescriptorProto as a raw byte slice
  2. Using @bufbuild/protobuf only for indexing (building symbol and dependency lookups)
  3. Serving the original raw bytes to reflection clients without any re-encoding

The result is byte-for-byte identical output to what buf build or protoc produced.

Installation

npm install grpc-descriptor-reflection

Peer dependencies (you likely already have these):

npm install @grpc/grpc-js @grpc/proto-loader

Generating a Descriptor Set

You need a binary FileDescriptorSet file. Generate one with either buf or protoc:

With buf (recommended)

buf build -o descriptor.bin --as-file-descriptor-set

With protoc

protoc \
  --descriptor_set_out=descriptor.bin \
  --include_imports \
  -I ./proto \
  ./proto/**/*.proto

Important: Use --include_imports (protoc) or the default behavior of buf build to include all transitive dependencies. The reflection service needs the full dependency graph to serve complete responses.

Usage

With vanilla @grpc/grpc-js

import * as grpc from "@grpc/grpc-js";
import { DescriptorReflectionService } from "grpc-descriptor-reflection";

const server = new grpc.Server();

// Add your application services...
// server.addService(MyService, myImplementation);

// Add reflection (registers both v1 and v1alpha endpoints)
const reflection = new DescriptorReflectionService("path/to/descriptor.bin");
reflection.addToServer(server);

server.bindAsync("0.0.0.0:50051", grpc.ServerCredentials.createInsecure(), () => {
  console.log("Server listening on port 50051");
});

With NestJS gRPC microservice

import { DescriptorReflectionService } from "grpc-descriptor-reflection";

const reflection = new DescriptorReflectionService(
  join(__dirname, "..", "descriptor.bin"),
);

@Module({
  imports: [
    ClientsModule.register([
      {
        name: "ORDERS_PACKAGE",
        transport: Transport.GRPC,
        options: {
          package: "orders.v1",
          protoPath: join(__dirname, "../proto/orders/v1/orders.proto"),
          url: "0.0.0.0:50051",
          onLoadPackageDefinition: (_pkg, server) => {
            reflection.addToServer(server);
          },
        },
      },
    ]),
  ],
})
export class AppModule {}

Filtering advertised services

By default, all services found in the descriptor set are advertised via ListServices. You can restrict which services are listed:

const reflection = new DescriptorReflectionService("descriptor.bin", {
  services: ["mypackage.MyService", "mypackage.AdminService"],
});

Note: The filter only affects ListServices responses. All symbols remain resolvable via FileContainingSymbol regardless of the filter.

API

new DescriptorReflectionService(descriptorSetPath, options?)

Creates a new reflection service instance.

Parameters:

  • descriptorSetPath (string) - Path to a binary-encoded FileDescriptorSet file.
  • options (DescriptorReflectionServiceOptions) - Optional configuration.
    • services (string[]) - Restrict which services are advertised via ListServices. If omitted, all services in the descriptor set are advertised.

reflection.addToServer(server)

Registers both grpc.reflection.v1.ServerReflection and grpc.reflection.v1alpha.ServerReflection services on the given server.

Parameters:

  • server - A grpc.Server instance or any object with an addService(service, implementation) method.

Supported Reflection Operations

| Operation | Description | |---|---| | ListServices | Lists all (or filtered) services | | FileContainingSymbol | Returns file descriptors for a fully-qualified symbol | | FileByFilename | Returns a file descriptor by its proto filename | | FileContainingExtension | Returns the file containing an extension field | | AllExtensionNumbersOfType | Returns all extension field numbers for a type |

All responses include transitive dependencies.

Comparison with @grpc/reflection

| Feature | @grpc/reflection | grpc-descriptor-reflection | |---|---|---| | Preserves google.api.http | No | Yes | | Preserves openapiv2_operation | No | Yes | | Preserves custom options | No | Yes | | Byte-for-byte fidelity | No | Yes | | Input | Proto file paths | Pre-built descriptor set | | Encoding library | protobufjs (lossy) | Raw wire format (lossless) |

How It Works

  1. The constructor reads the binary FileDescriptorSet and parses the protobuf wire format directly to extract each FileDescriptorProto as a raw Uint8Array slice.
  2. It then decodes the set with @bufbuild/protobuf (which does preserve extensions) solely to build indexes: symbol-to-file mappings, dependency graphs, and extension registries.
  3. When a reflection client requests a file, the service serves the original raw bytes extracted in step 1, along with all transitive dependencies.

Since the raw bytes are never decoded and re-encoded, every extension field, custom option, and annotation is preserved exactly as the proto compiler produced it.

License

MIT