Tools for loading Protocol Buffer definitions and generating client constructors for type-safe service communication, enabling dynamic service discovery and client generation from proto files.
Load Protocol Buffer package definitions into gRPC service objects for client and server use.
/**
* Load a package definition from proto-loader into gRPC objects
* @param packageDef - Package definition from @grpc/proto-loader
* @returns Hierarchical object containing service definitions
*/
function loadPackageDefinition(packageDef: PackageDefinition): GrpcObject;Create client constructor functions from service definitions for type-safe service communication.
/**
* Create a client constructor from a service definition
* @param methods - Service definition containing method definitions
* @param serviceName - Name of the service for error messages
* @param classOptions - Additional options for the generated class
* @returns Constructor function for creating service clients
*/
function makeClientConstructor(
methods: ServiceDefinition,
serviceName: string,
classOptions?: {}
): ServiceClientConstructor;
/**
* Alias for makeClientConstructor (deprecated name)
*/
const makeGenericClientConstructor: typeof makeClientConstructor;Core interfaces for defining gRPC services and methods.
/**
* Definition of a complete gRPC service
*/
interface ServiceDefinition {
[methodName: string]: MethodDefinition<any, any>;
}
/**
* Definition of a single gRPC method
*/
interface MethodDefinition<RequestType, ResponseType> {
/** Full path of the method (e.g., /package.Service/Method) */
path: string;
/** Whether the client streams multiple requests */
requestStream: boolean;
/** Whether the server streams multiple responses */
responseStream: boolean;
/** Function to serialize request messages */
requestSerialize: Serialize<RequestType>;
/** Function to deserialize response messages */
responseDeserialize: Deserialize<ResponseType>;
/** Original method name from proto file */
originalName?: string;
}
/**
* Client-specific method definition
*/
interface ClientMethodDefinition<RequestType, ResponseType> extends MethodDefinition<RequestType, ResponseType> {
/** Response serializer (for server streaming) */
responseSerialize: Serialize<ResponseType>;
/** Request deserializer (for client streaming) */
requestDeserialize: Deserialize<RequestType>;
}
/**
* Server-specific method definition
*/
interface ServerMethodDefinition<RequestType, ResponseType> extends MethodDefinition<RequestType, ResponseType> {
/** Request deserializer */
requestDeserialize: Deserialize<RequestType>;
/** Response serializer */
responseSerialize: Serialize<ResponseType>;
}Type definitions for message serialization and deserialization.
/**
* Function to serialize messages to Buffer
*/
interface Serialize<T> {
(value: T): Buffer;
}
/**
* Function to deserialize messages from Buffer
*/
interface Deserialize<T> {
(bytes: Buffer): T;
}Interfaces for generated client constructors and instances.
/**
* Constructor function for service clients
*/
interface ServiceClientConstructor {
new (address: string, credentials: ChannelCredentials, options?: Partial<ClientOptions>): ServiceClient;
service: ServiceDefinition;
}
/**
* Generated service client interface
*/
interface ServiceClient extends Client {
[methodName: string]: any; // Dynamic methods based on service definition
}
/**
* Hierarchical object containing services and nested packages
*/
interface GrpcObject {
[name: string]: GrpcObject | ServiceClientConstructor | ProtobufTypeDefinition;
}
/**
* Package definition input from proto-loader
*/
interface PackageDefinition {
[fullyQualifiedName: string]: ServiceDefinition | ProtobufTypeDefinition;
}
/**
* Protobuf type definition for messages and enums
*/
interface ProtobufTypeDefinition {
format: string;
type: object;
fileDescriptorProtos: Buffer[];
}import { loadSync } from "@grpc/proto-loader";
import { loadPackageDefinition, credentials } from "@grpc/grpc-js";
// Load proto file using proto-loader
const packageDefinition = loadSync("path/to/service.proto", {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
// Load into gRPC
const protoDescriptor = loadPackageDefinition(packageDefinition);
// Access service constructor
const MyServiceClient = protoDescriptor.mypackage.MyService;
// Create client instance
const client = new MyServiceClient(
"localhost:50051",
credentials.createInsecure()
);import { makeClientConstructor, credentials, status } from "@grpc/grpc-js";
// Define service manually (usually comes from proto loading)
const serviceDefinition: ServiceDefinition = {
sayHello: {
path: '/greeter.Greeter/SayHello',
requestStream: false,
responseStream: false,
requestSerialize: (value: any) => Buffer.from(JSON.stringify(value)),
responseDeserialize: (bytes: Buffer) => JSON.parse(bytes.toString())
},
streamGreetings: {
path: '/greeter.Greeter/StreamGreetings',
requestStream: false,
responseStream: true,
requestSerialize: (value: any) => Buffer.from(JSON.stringify(value)),
responseDeserialize: (bytes: Buffer) => JSON.parse(bytes.toString())
}
};
// Create client constructor
const GreeterClient = makeClientConstructor(
serviceDefinition,
'GreeterService'
);
// Use the generated client
const client = new GreeterClient(
"localhost:50051",
credentials.createInsecure()
);
// Make unary call
client.sayHello({ name: "World" }, (error, response) => {
if (error) {
console.error("Error:", error);
return;
}
console.log("Response:", response);
});
// Make streaming call
const stream = client.streamGreetings({ count: 5 });
stream.on('data', (response) => {
console.log("Greeting:", response);
});import { loadSync } from "@grpc/proto-loader";
import {
loadPackageDefinition,
credentials,
Server,
ServerCredentials
} from "@grpc/grpc-js";
// Load proto file
const PROTO_PATH = "./protos/greeter.proto";
const packageDefinition = loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: ["./protos"] // Additional proto directories
});
const grpcObject = loadPackageDefinition(packageDefinition);
// Access nested service (e.g., com.example.greeter.GreeterService)
const GreeterService = grpcObject.com?.example?.greeter?.GreeterService;
if (!GreeterService) {
throw new Error("Service not found in proto definition");
}
// Create server and add service
const server = new Server();
server.addService(GreeterService.service, {
sayHello: (call, callback) => {
callback(null, {
message: `Hello ${call.request.name}!`
});
},
sayHelloStream: (call) => {
const name = call.request.name;
for (let i = 0; i < 5; i++) {
call.write({
message: `Hello ${name} #${i + 1}`
});
}
call.end();
}
});
// Start server
server.bindAsync(
"0.0.0.0:50051",
ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
console.error("Server bind failed:", error);
return;
}
console.log(`Server running on port ${port}`);
server.start();
}
);
// Create client
const client = new GreeterService(
"localhost:50051",
credentials.createInsecure()
);
// Use client
client.sayHello({ name: "gRPC" }, (error, response) => {
console.log("Server response:", response?.message);
});import { makeClientConstructor, ServiceDefinition } from "@grpc/grpc-js";
// Complex service with all call types
const advancedServiceDefinition: ServiceDefinition = {
// Unary call
getUser: {
path: '/users.UserService/GetUser',
requestStream: false,
responseStream: false,
requestSerialize: serializeUserRequest,
responseDeserialize: deserializeUser,
originalName: 'GetUser'
},
// Client streaming
uploadFiles: {
path: '/files.FileService/UploadFiles',
requestStream: true,
responseStream: false,
requestSerialize: serializeFileChunk,
responseDeserialize: deserializeUploadResponse
},
// Server streaming
watchEvents: {
path: '/events.EventService/WatchEvents',
requestStream: false,
responseStream: true,
requestSerialize: serializeEventQuery,
responseDeserialize: deserializeEvent
},
// Bidirectional streaming
chat: {
path: '/chat.ChatService/Chat',
requestStream: true,
responseStream: true,
requestSerialize: serializeChatMessage,
responseDeserialize: deserializeChatMessage
}
};
// Create typed client
const ServiceClient = makeClientConstructor(
advancedServiceDefinition,
'AdvancedService'
);
// Custom serialization functions
function serializeUserRequest(request: { id: string }): Buffer {
return Buffer.from(JSON.stringify(request));
}
function deserializeUser(bytes: Buffer): { id: string; name: string; email: string } {
return JSON.parse(bytes.toString());
}
function serializeFileChunk(chunk: { filename: string; data: Buffer }): Buffer {
// Custom serialization logic
return Buffer.concat([
Buffer.from(chunk.filename.length.toString().padStart(4, '0')),
Buffer.from(chunk.filename),
chunk.data
]);
}import { loadSync, loadFileDescriptorSetFromBuffer } from "@grpc/proto-loader";
import { loadPackageDefinition } from "@grpc/grpc-js";
// Load with custom options
const packageDefinition = loadSync("service.proto", {
keepCase: true, // Keep original field names
longs: String, // Convert longs to strings
enums: String, // Convert enums to strings
bytes: Array, // Convert bytes to arrays
defaults: true, // Include default values
arrays: true, // Use arrays for repeated fields
objects: true, // Use objects for maps
oneofs: true, // Include oneof fields
includeDirs: [ // Additional proto directories
"./protos",
"./vendor/protos"
]
});
// Load from descriptor set (compiled protos)
const descriptorSetBuffer = readFileSync("compiled.pb");
const packageDefinitionFromDescriptor = loadFileDescriptorSetFromBuffer(
descriptorSetBuffer
);
// Load both into gRPC
const services = loadPackageDefinition(packageDefinition);
const compiledServices = loadPackageDefinition(packageDefinitionFromDescriptor);
// Handle deeply nested packages
const deepService = services.com?.company?.division?.team?.ServiceName;
if (deepService && typeof deepService === 'function') {
const client = new deepService(address, credentials.createInsecure());
}// Type-safe service definition creation
interface HelloRequest {
name: string;
}
interface HelloResponse {
message: string;
}
const typedServiceDefinition: ServiceDefinition = {
sayHello: {
path: '/greeter.Greeter/SayHello',
requestStream: false,
responseStream: false,
requestSerialize: (req: HelloRequest) =>
Buffer.from(JSON.stringify(req)),
responseDeserialize: (bytes: Buffer): HelloResponse =>
JSON.parse(bytes.toString()),
originalName: 'SayHello'
}
};
// Generated client will be type-safe
const TypedClient = makeClientConstructor(
typedServiceDefinition,
'TypedGreeter'
);
const client = new TypedClient(address, credentials.createInsecure());
// TypeScript will infer correct types
client.sayHello({ name: "TypeScript" }, (error, response) => {
if (response) {
console.log(response.message); // TypeScript knows this is a string
}
});