Pure JavaScript gRPC implementation for Node.js with comprehensive client-server communication capabilities
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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
}
});