Handle Proto
The loader carries a few low-level helpers for inspecting parsed proto content directly. They’re the same primitives gRPCity uses internally for add(serviceName, impl)-style calls — you’ll reach for them when you need to introspect services, look up messages by their fully qualified name, or wire up tooling against a loaded proto tree.
Load the proto
Take protos/test/helloworld/ as the proto root with this content:
service Greeter {
// Sends a greeting
rpc SayHello(model.HelloRequest) returns (model.HelloReply) {}
rpc SayHello2(model.HelloRequest) returns (model.HelloReply) {}
}We need to load the proto first.
import { ProtoLoader } from 'grpcity'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const loader = new ProtoLoader({
location: path.resolve(__dirname, 'protos'),
files: ['test/helloworld/helloworld.proto']
})
(async () => {
await loader.init()
})()Package prefix
If you need to namespace the loaded definitions — typically for tenant or environment sandboxing — pass isDev and packagePrefix to init():
await loader.init({
isDev: true,
packagePrefix: 'stage.dev'
})Once a prefix is set, every loaded definition lives under it — including the on-wire method paths. Server and client must agree on the same prefix.
service(name)
name is the fully qualified service name from the .proto, including the package. The returned object is the underlying gRPC service definition.
const serviceDefinition = loader.service('test.helloworld.Greeter')
console.log(serviceDefinition)Prints:
{
SayHello: {
path: '/stage.dev.test.helloworld.Greeter/SayHello',
requestStream: false,
responseStream: false,
requestSerialize: [Function: serialize],
requestDeserialize: [Function: deserialize],
responseSerialize: [Function: serialize],
responseDeserialize: [Function: deserialize],
originalName: 'sayHello',
requestType: {
format: 'Protocol Buffer 3 DescriptorProto',
type: [Object],
fileDescriptorProtos: [Array]
},
responseType: {
format: 'Protocol Buffer 3 DescriptorProto',
type: [Object],
fileDescriptorProtos: [Array]
}
},
SayHello2: {
path: '/stage.dev.test.helloworld.Greeter/SayHello2',
requestStream: false,
responseStream: false,
requestSerialize: [Function: serialize],
requestDeserialize: [Function: deserialize],
responseSerialize: [Function: serialize],
responseDeserialize: [Function: deserialize],
originalName: 'sayHello2',
requestType: {
format: 'Protocol Buffer 3 DescriptorProto',
type: [Object],
fileDescriptorProtos: [Array]
},
responseType: {
format: 'Protocol Buffer 3 DescriptorProto',
type: [Object],
fileDescriptorProtos: [Array]
}
}
}type(name)
name is any fully qualified name in the loaded proto tree — packages, services, models, or messages. type() is a superset of service(): anything you can pass to service() also resolves through type().
Example with type('test'):
const typePkg = loader.type('test')
console.log(typePkg)Prints:
{
helloworld: {
Greeter: [class ServiceClientImpl extends Client] {
service: [Object],
serviceName: 'Greeter'
},
Hellor: [class ServiceClientImpl extends Client] {
service: [Object],
serviceName: 'Hellor'
},
model: { HelloRequest: [Object], HelloReply: [Object] }
}
}Example with type('test.helloworld'):
const typePkg = loader.type('test.helloworld')
console.log(typePkg)Prints:
{
Greeter: [class ServiceClientImpl extends Client] {
service: { SayHello: [Object], SayHello2: [Object] },
serviceName: 'Greeter'
},
Hellor: [class ServiceClientImpl extends Client] {
service: { SayHello: [Object], SayHello2: [Object] },
serviceName: 'Hellor'
},
model: {
HelloRequest: {
format: 'Protocol Buffer 3 DescriptorProto',
type: [Object],
fileDescriptorProtos: [Array]
},
HelloReply: {
format: 'Protocol Buffer 3 DescriptorProto',
type: [Object],
fileDescriptorProtos: [Array]
}
}
}