{"id":22344492,"url":"https://github.com/ayonli/grpc-async","last_synced_at":"2026-01-21T07:33:40.143Z","repository":{"id":184441341,"uuid":"671889710","full_name":"ayonli/grpc-async","owner":"ayonli","description":"A gRPC wrapper for Node.js with async functions.","archived":false,"fork":false,"pushed_at":"2024-08-04T17:04:42.000Z","size":370,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-23T07:27:12.748Z","etag":null,"topics":["grpc"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ayonli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-28T11:30:06.000Z","updated_at":"2024-08-04T17:04:46.000Z","dependencies_parsed_at":"2023-07-28T12:38:43.779Z","dependency_job_id":"fc3be4c8-a884-464f-8289-11418b196927","html_url":"https://github.com/ayonli/grpc-async","commit_stats":null,"previous_names":["hyurl/grpc-async","ayonli/grpc-async"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ayonli/grpc-async","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fgrpc-async","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fgrpc-async/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fgrpc-async/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fgrpc-async/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ayonli","download_url":"https://codeload.github.com/ayonli/grpc-async/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayonli%2Fgrpc-async/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28629915,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["grpc"],"created_at":"2024-12-04T09:12:03.448Z","updated_at":"2026-01-21T07:33:40.124Z","avatar_url":"https://github.com/ayonli.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gRPC Async\n\nA gRPC wrapper for Node.js with async functions.\n\nThe design of [@grpc/grpc-js](https://www.npmjs.com/package/@grpc/grpc-js) uses\na traditional Node.js callback design for function implementations, which, in\nnowadays programming world, is painful. So this little library wraps the\nasync/await functionality into both the gRPC server and the client.\n\n*(NOTE: this package only supports dynamic generated version of gRPC functions*\n*since the static generated version lacks typing support.)*\n\n## Prerequisites\n\n- [Node.js](https://nodejs.org) v14+\n- [@grpc/grpc-js](https://www.npmjs.com/package/@grpc/grpc-js) and [@grpc/proto-loader](https://www.npmjs.com/package/@grpc/proto-loader)\n- For server-side code, if using [TypeScript](https://www.typescriptlang.org/),\n    make sure the compiling target is `es2018` or higher which outputs native\n    async generator functions.\n\n## Install\n\nIn order to install this package, we must install **@grpc/grpc-js** and\n**@grpc/proto-loader** as well.\n\n```sh\nnpm i @grpc/grpc-js @grpc/proto-loader @ayonli/grpc-async\n```\n\n## Examples\n\nThe following examples first show the traditional way of implementations and\nthen show the async way implementations, so that we can compare how simple it is\nin the new way.\n\n### Traditional Way\n\n```ts\nimport * as protoLoader from '@grpc/proto-loader';\nimport {\n    loadPackageDefinition,\n    GrpcObject,\n    ServiceClientConstructor,\n    Server,\n    ServerUnaryCall,\n    ServerWritableStream,\n    ServerReadableStream,\n    ServerDuplexStream,\n    ServerCredentials,\n    credentials,\n    ClientReadableStream,\n    ClientWritableStream,\n    ClientDuplexStream\n} from \"@grpc/grpc-js\"\n\nconst PROTO_PATH = __dirname + '/examples/Greeter.proto';\nconst SERVER_ADDRESS = \"localhost:50051\";\n\nconst packageDefinition = protoLoader.loadSync(PROTO_PATH, {\n    longs: String,\n    enums: String,\n    defaults: true,\n    oneofs: true\n});\nconst examples = loadPackageDefinition(packageDefinition).examples as GrpcObject;\nconst Greeter = examples.Greeter as ServiceClientConstructor;\n\ntype Request = {\n    name: string;\n};\n\ntype Response = {\n    message: string;\n};\n\n// ==== server ====\nconst server = new Server();\nserver.addService(Greeter.service, {\n    sayHello: (\n        call: ServerUnaryCall\u003cRequest, Response\u003e,\n        callback: (err: Error, reply: Response) =\u003e void\n    ) =\u003e {\n        const { name } = call.request;\n        callback(null, { message: \"Hello, \" + name } as Response);\n    },\n    sayHelloStreamReply: (call: ServerWritableStream\u003cRequest, Response\u003e) =\u003e {\n        const { name } = call.request;\n        call.write({ message: \"Hello 1: \" + name } as Response);\n        call.write({ message: \"Hello 2: \" + name } as Response);\n        call.write({ message: \"Hello 3: \" + name } as Response);\n        call.end();\n    },\n    sayHelloStreamRequest: (call: ServerReadableStream\u003cRequest, Response\u003e, callback) =\u003e {\n        const names: string[] = [];\n\n        call.on(\"data\", ({ name }: Request) =\u003e {\n            names.push(name);\n        }).once(\"end\", () =\u003e {\n            callback(null, { message: \"Hello, \" + names.join(\", \") } as Response);\n        }).once(\"error\", (err) =\u003e {\n            callback(err, void 0);\n        });\n    },\n    sayHelloDuplex: (call: ServerDuplexStream\u003cRequest, Response\u003e) =\u003e {\n        call.on(\"data\", ({ name }: Request) =\u003e {\n            call.write({ message: \"Hello, \" + name });\n        });\n    }\n});\n\nserver.bindAsync(SERVER_ADDRESS, ServerCredentials.createInsecure(), () =\u003e {\n    server.start();\n});\n// ==== server ====\n\n// ==== client ====\nconst client = new Greeter(SERVER_ADDRESS, credentials.createInsecure());\n\n// Calling #waitForReady() is required since at this point the server may not be\n// available yet.\nclient.waitForReady(Date.now() + 5000, (_: Error) =\u003e {\n    client.sayHello({ name: \"World\" } as Request, (err: Error, reply: Response) =\u003e {\n        if (err) {\n            console.error(err);\n        } else {\n            console.log(reply); // { message: \"Hello, World\" }\n        }\n    });\n\n    const streamReplyCall: ClientReadableStream\u003cResponse\u003e = client.sayHelloStreamReply({\n        name: \"World\",\n    } as Request);\n    streamReplyCall.on(\"data\", (reply: Response) =\u003e {\n        console.log(reply);\n        // { message: \"Hello 1: World\" }\n        // { message: \"Hello 2: World\" }\n        // { message: \"Hello 3: World\" }\n    }).on(\"error\", err =\u003e {\n        console.error(err);\n    });\n\n    const streamRequestCall: ClientWritableStream\u003cRequest\u003e = client.sayHelloStreamRequest(\n        (err: Error, reply: Response) =\u003e {\n            if (err) {\n                console.error(err);\n            } else {\n                console.log(reply); // { message: \"Hello, Mr. World, Mrs. World\" }\n\n                // THINK: what should we do with the **reply**? If our code\n                // logic is from top to bottom, but we get the reply above the\n                // logic.\n            }\n        }\n    );\n    streamRequestCall.write({ name: \"Mr. World\" } as Request);\n    streamRequestCall.write({ name: \"Mrs. World\" } as Request);\n    streamRequestCall.end();\n\n    const duplexCall: ClientDuplexStream\u003cRequest, Response\u003e = client.sayHelloDuplex();\n    duplexCall.on(\"data\", (reply: Response) =\u003e {\n        console.log(reply);\n        // { message: \"Hello, Mr. World\" }\n        // { message: \"Hello, Mrs. World\" }\n    });\n    duplexCall.write({ name: \"Mr. World\" });\n    duplexCall.write({ name: \"Mrs. World\" });\n    duplexCall.end();\n});\n// ==== client ====\n```\n\n### Async Way\n\n```ts\nimport * as protoLoader from '@grpc/proto-loader';\nimport {\n    loadPackageDefinition,\n    GrpcObject,\n    ServiceClientConstructor,\n    Server,\n    ServerCredentials,\n    credentials\n} from \"@grpc/grpc-js\";\nimport {\n    serve,\n    connect,\n    ServerReadableStream,\n    ServerDuplexStream\n} from \"@ayonli/grpc-async\";\n\nconst PROTO_PATH = __dirname + '/examples/Greeter.proto';\nconst SERVER_ADDRESS = \"localhost:50051\";\n\nconst packageDefinition = protoLoader.loadSync(PROTO_PATH, {\n    keepCase: true,\n    longs: String,\n    enums: String,\n    defaults: true,\n    oneofs: true\n});\nconst examples = loadPackageDefinition(packageDefinition).examples as GrpcObject;\n\ntype Request = {\n    name: string;\n};\n\ntype Response = {\n    message: string;\n};\n\nclass Greeter {\n    async sayHello({ name }: Request) {\n        return { message: 'Hello ' + name } as Response;\n    }\n\n    async *sayHelloStreamReply({ name }: Request) {\n        yield { message: `Hello 1: ${name}` } as Response;\n        yield { message: `Hello 2: ${name}` } as Response;\n        yield { message: `Hello 3: ${name}` } as Response;\n    }\n\n    async sayHelloStreamRequest(stream: ServerReadableStream\u003cRequest, Response\u003e) {\n        const names: string[] = [];\n\n        for await (const { name } of stream) {\n            names.push(name);\n        }\n\n        return await this.sayHello({ name: names.join(\", \") });\n    }\n\n    async *sayHelloDuplex(stream: ServerDuplexStream\u003cRequest, Response\u003e) {\n        for await (const req of stream) {\n            yield await this.sayHello(req);\n        }\n    }\n}\n\n// ==== server ====\nconst server = new Server()\n\nserve(server, examples.Greeter as ServiceClientConstructor, new Greeter());\n\nserver.bindAsync(SERVER_ADDRESS, ServerCredentials.createInsecure(), () =\u003e {\n    server.start();\n});\n// ==== server ====\n\n// ==== client ====\nconst client = connect\u003cGreeter\u003e(\n    examples.Greeter as ServiceClientConstructor,\n    SERVER_ADDRESS,\n    credentials.createInsecure());\n\n(async () =\u003e {\n    const reply = await client.sayHello({ name: \"World\" });\n    console.log(reply); // { message: \"Hello, World\" }\n})().catch(console.error);\n\n(async () =\u003e {\n    for await (const reply of client.sayHelloStreamReply({ name: \"World\" })) {\n        console.log(reply);\n        // { message: \"Hello 1: World\" }\n        // { message: \"Hello 2: World\" }\n        // { message: \"Hello 3: World\" }\n    }\n})().catch(console.error);\n\n(async () =\u003e {\n    const call = client.sayHelloStreamRequest();\n    call.write({ name: \"Mr. World\" });\n    call.write({ name: \"Mrs. World\" });\n\n    const reply = await call.returns();\n    console.log(reply); // { message: \"Hello, Mr. World, Mrs. World\" }\n})().catch(console.error);\n\n(async () =\u003e {\n    const call = client.sayHelloDuplex();\n    let counter = 0;\n\n    call.write({ name: \"Mr. World\" });\n    call.write({ name: \"Mrs. World\" });\n\n    for await (const reply of call) {\n        console.log(reply);\n        // { message: \"Hello, Mr. World\" }\n        // { message: \"Hello, Mrs. World\" }\n\n        if (++counter === 2) {\n            call.end(); // this will cause the iterator to close\n        }\n    }\n})().catch(console.error);\n// ==== client ====\n```\n\n*We can see more about the examples in the [examples](./examples) folder.*\n\n#### Recap\n\nSee the major differences here?\n\n**On the server**\n\n1. Instead of calling the `server.addService()` function to register the\n    implementation, we use the `serve()` utility function, which supports\n    native async (and async generator) functions.\n2. Instead of just using an object literal as the service implementation, we\n    define a class as implementation that honors the design in the `.proto` file.\n3. Instead of accessing the `request` from the `call` context argument, we\n    receive the data directly from the function's argument, which honor the same\n    design in the `.proto` file.\n4. For unary calls, instead of calling the `callback()` to send the response, we\n    simply return it from the function, which also honor the same design in the\n    `.proto` file.\n5. For stream reply calls, instead of calling `call.write()` to send the\n    response, we take advantage of the native `yield` expression, which\n    generates results overtime.\n6. For stream request calls, instead of listening to the `data` and `end` events,\n    we use the `for await` statement to receive the requests sent by the client.\n7. For duplex calls, instead of listening to the `data` event for requests and\n    calling `call.write()` to send back response, we use the `for await`\n    statement and the `yield` expression which are more straightforward.\n\n**On the client**\n\n1. Instead of creating the instance via a `new` expression, we use `connect()`\n    utility function to generate the instance, which resolves RPC functions with\n    native async support.\n2. We use the type `Greeter` on the `connect()` function so it can produce\n    correct methods in TypeScript that could helps us reduce errors in our code.\n3. There is no need for the `waitForReady()` since it's handled automatically\n    inside the function call.\n4. For unary calls, instead of passing a callback function to retrieve the\n    response, we use the `await` expression to get the result.\n5. For stream reply calls, instead of listening to the `data` and `end` events,\n    we use the `for await` statement to receive the responses yielded by the\n    server.\n6. For stream request calls, instead using a callback function to receive the\n    response, we use the `call.returns()` to retrieve the response just where\n    we need it.\n7. For duplex calls, instead of listening to the `data` event for responses,\n    again, we use the `for await` statement to receive the responses yielded by\n    the server.\n\n## LoadBalancer\n\nOther than using `connect()` to connect to a certain server, we can use\n`new LoadBalancer()` to connect to multiple servers at once and leverage calls\nwith a programmatic client-side load balancer.\n\nUnlike the traditional load balancer which uses a DNS resolver that assumes our\nprogram runs on different machines or virtual machines, this new load balancer\nallows us to run the server in the same machine but in many processes/instances,\nand we can programmatically control how our traffic is routed to different\nserver instances on demand.\n\n```ts\nimport { LoadBalancer } from \"@ayonli/grpc-async\";\n// ...\n\n// Imagine we have three server instances run on the same server (localhost).\nconst balancer = new LoadBalancer(examples.Greeter as ServiceClientConstructor, [\n    { address: \"localhost:50051\", credentials: credentials.createInsecure() },\n    { address: \"localhost:50052\", credentials: credentials.createInsecure() },\n    { address: \"localhost:50053\", credentials: credentials.createInsecure() }\n]);\n\n(async () =\u003e {\n    // Be default, the load balancer uses round-robin algorithm for routing, so\n    // this call happens on the first server instance,\n    const reply1 = await balancer.getInstance().sayHello({ name: \"World\" });\n\n    // this call happens on the second server instance.\n    const reply2 = await balancer.getInstance().sayHello({ name: \"World\" });\n\n    // this call happens on the third server instance.\n    const reply3 = await balancer.getInstance().sayHello({ name: \"World\" });\n\n    // this call happens on the first server instance.\n    const reply4 = await balancer.getInstance().sayHello({ name: \"World\" });\n})();\n\n// We can define the route resolver to achieve custom load balancing strategy.\nimport hash from \"string-hash\"; // assuming this package exists\nconst balancer2 = new LoadBalancer(examples.Greeter as ServiceClientConstructor, [\n    { address: \"localhost:50051\", credentials: credentials.createInsecure() },\n    { address: \"localhost:50052\", credentials: credentials.createInsecure() },\n    { address: \"localhost:50053\", credentials: credentials.createInsecure() }\n], (ctx) =\u003e {\n    const addresses: string[] = ctx.servers.map(item =\u003e item.address);\n\n    if (typeof ctx.params === \"string\") {\n        if (addresses.includes(ctx.params)) {\n            return ctx.params; // explicitly use a server instance\n        } else {\n            // route by hash\n            const id: number = hash(ctx.params);\n            return addresses[id % addresses.length];\n        }\n    } else if (typeof ctx.params === \"number\") {\n        return addresses[ctx.params % addresses.length];\n    } else if (typeof ctx.params === \"object\") {\n        // This algorithm guarantees the same param structure passed to the\n        // `getInstance()` returns the same service instance.\n        const id: number = hash(String(Object.keys(ctx.params ?? {}).sort()));\n        return addresses[id % addresses.length];\n    } else {\n        // use round-robin\n        return addresses[ctx.acc % addresses.length];\n    }\n});\n\n(async () =\u003e {\n    // These two calls will happen on the same server instance since they have\n    // the same route param structure:\n    const req1: Request = { name: \"Mr. World\" };\n    const reply1 = await balancer2.getInstance(req).sayHello(req);\n\n    const req2: Request = { name: \"Mrs. World\" };\n    const reply2 = await balancer2.getInstance(req).sayHello(req);\n\n    // This call happens on the first server since we explicitly set the server\n    // address to use:\n    const req3: Request = { name: \"Mrs. World\" };\n    const reply3 = await balancer2.getInstance(\"localhost:50051\").sayHello(req);\n})();\n```\n\n## ConnectionManager\n\nConnectionManager provides a place to manage all clients and retrieve instances\nvia a general approach.\n\nA client or a load balancer always binds a specific service client constructor\nand is a scoped variable, if we are going to use them across our program in\ndifferent places, it would very painful and may cause recursive import problem.\n\nThe connection manager, however, is a central place and a single variable, we\ncan assign it to the global namespace and use it to retrieve service instances\nanywhere we want without worrying how to import them.\n\nFor example:\n\n```ts\nimport { ConnectionManager } from \"@ayonli/grpc-async\";\n// ...\n\ndeclare global {\n    const services: ConnectionManager;\n}\n\n// @ts-ignore\nconst manager = global[\"services\"] = new ConnectionManager();\n\nmanager.register(client);\n// Or\nmanager.register(balancer);\n\n// and use it anywhere\nconst ins = services.getInstanceOf\u003cGreeter\u003e(\"examples.Greeter\");\nconst result = await ins.sayHello({ name: \"World\" });\n```\n\n**Further more**, we can extend our `services` via chaining syntax, make our code\neven more cleaner and elegant.\n\n```ts\nimport { ConnectionManager, ServiceClient } from \"@ayonli/grpc-async\";\n// ...\n\ndeclare global {\n    // Instead of defining `services` as global constant, we define it as a\n    // namespace which contains sub namespaces that corresponds the package name\n    // in the .proto file.\n    namespace services.examples {\n        const Greeter: ServiceClient\u003cGreeter\u003e;\n    }\n}\n\nconst manager = new ConnectionManager();\n\nmanager.register(client);\n// Or\nmanager.register(balancer);\n\n// @ts-ignore\nglobal[\"services\"] = manager.useChainingSyntax();\n\n// and use it anywhere\nconst result = await services.examples.Greeter.sayHello({ name: \"World\" });\n```\n\nFor more information about the `LoadBalancer` and the `ConnectionManager`, please\nrefer to the [source code](./client.ts) of their definition. They are the\nenhancement part of this package that aims to provide straightforward usage of\ngRPC in a project with distributed system design.\n\n## API\n\nApart from the functions and classes, for better TypeScript support, this package\nalso rewrites some of the interfaces/types seen in the **@grpc/grpc-js** library,\nI'll list them all as follows:\n\n```ts\nimport * as grpc from \"@grpc/grpc-js\";\n\nexport declare function serve\u003cT\u003e(\n    server: grpc.Server,\n    service: grpc.ServiceClientConstructor | grpc.ServiceDefinition\u003cT\u003e,\n    instance: T\n): void;\n\nexport declare function unserve\u003cT\u003e(\n    server: grpc.Server,\n    service: grpc.ServiceClientConstructor | grpc.ServiceDefinition\u003cT\u003e\n): void;\n\nexport declare function connect\u003cT\u003e(\n    service: grpc.ServiceClientConstructor,\n    address: string,\n    credentials: grpc.ChannelCredentials,\n    options?: Partial\u003cgrpc.ChannelOptions\u003e \u0026 {\n        connectTimeout?: number; // default 120_000 ms\n    }\n): ServiceClient\u003cT\u003e;\n\nexport type ServerWritableStream\u003cReq, Res\u003e = grpc.ServerWritableStream\u003cReq, Res\u003e;\n\nexport type ServerReadableStream\u003cReq, Res = void\u003e = grpc.ServerReadableStream\u003cReq, Res\u003e \u0026 AsyncIterable\u003cReq\u003e;\n\nexport type ServerDuplexStream\u003cReq, Res\u003e = grpc.ServerDuplexStream\u003cReq, Res\u003e \u0026 AsyncIterable\u003cReq\u003e;\n\nexport type ClientWritableStream\u003cReq, Res\u003e = grpc.ClientWritableStream\u003cReq\u003e \u0026 {\n    returns(): Promise\u003cRes\u003e;\n};\n\nexport type ClientReadableStream\u003cRes\u003e = grpc.ClientReadableStream\u003cRes\u003e \u0026 AsyncIterable\u003cRes\u003e;\n\nexport type ClientDuplexStream\u003cReq, Res\u003e = grpc.ClientDuplexStream\u003cReq, Res\u003e \u0026 AsyncIterable\u003cRes\u003e;\n\nexport type UnaryFunction\u003cReq, Res\u003e = (req: Req, metadata?: grpc.Metadata) =\u003e Promise\u003cRes\u003e;\n\nexport type StreamResponseFunction\u003cReq, Res\u003e = (req: Req, metadata?: grpc.Metadata) =\u003e AsyncGenerator\u003cRes, void, unknown\u003e;\n\nexport type StreamRequestFunction\u003cReq, Res\u003e = (stream: ServerReadableStream\u003cReq\u003e) =\u003e Promise\u003cRes\u003e;\n\nexport type DuplexFunction\u003cReq, Res\u003e = (stream: ServerDuplexStream\u003cReq, Res\u003e) =\u003e AsyncGenerator\u003cRes, void, unknown\u003e;\n\nexport type ClientMethods\u003cT extends object\u003e = {\n    [K in keyof T]: T[K] extends DuplexFunction\u003cinfer Req, infer Res\u003e ? (metadata?: grpc.Metadata) =\u003e ClientDuplexStream\u003cReq, Res\u003e\n    : T[K] extends StreamRequestFunction\u003cinfer Req, infer Res\u003e ? (metadata?: grpc.Metadata) =\u003e ClientWritableStream\u003cReq, Res\u003e\n    : T[K] extends StreamResponseFunction\u003cinfer Req, infer Res\u003e ? (req: Req, metadata?: grpc.Metadata) =\u003e AsyncGenerator\u003cRes, void, unknown\u003e\n    : T[K] extends UnaryFunction\u003cinfer Req, infer Res\u003e ? (req: Req, metadata?: grpc.Metadata) =\u003e Promise\u003cRes\u003e\n    : T[K];\n};\n\nexport type ServiceClient\u003cT extends object\u003e = Omit\u003cgrpc.Client, \"waitForReady\"\u003e \u0026 {\n    waitForReady(deadline?: Date | number): Promise\u003cvoid\u003e;\n    waitForReady(deadline: Date | number, callback: (err: Error) =\u003e void): void;\n} \u0026 ClientMethods\u003cT\u003e;\n\nexport type ServerConfig = {\n    address: string;\n    credentials: grpc.ChannelCredentials,\n    options?: Partial\u003cgrpc.ChannelOptions\u003e \u0026 { connectTimeout?: number; };\n};\n\nexport declare class LoadBalancer\u003cT extends object, P extends any = any\u003e {\n    readonly service: grpc.ServiceClientConstructor;\n\n    /**\n     * @param target \n     * @param servers The server configurations used to create service client.\n     * @param routeResolver Custom route resolver used to implement load\n     *  balancing algorithms, if not provided, a default round-robin algorithm\n     *  is used. The function takes a context object and returns an address\n     *  filtered from the `ctx.servers`.\n     */\n    constructor(\n        service: grpc.ServiceClientConstructor,\n        servers: ServerConfig[],\n        routeResolver?: ((ctx: {\n            service: grpc.ServiceClientConstructor;\n            servers: (ServerConfig \u0026 { state: grpc.connectivityState; })[];\n            /**\n             * The route params passed when calling the `getInstance()` function, we\n             * can use this object to calculate the desired route address.\n             */\n            params: P | null;\n            acc: number;\n        }) =\u003e string)\n    );\n\n    /**\n     * Dynamically add server configurations at runtime, this is useful when we \n     * need to implement some kind of service discovery strategy.\n     */\n    addServer(server: ServerConfig): boolean;\n\n    /**\n     * Dynamically remove server configurations at runtime, this is useful when we \n     * need to implement some kind of service discovery strategy.\n     */\n    removeServer(address: string): boolean;\n\n    /**\n     * Retrieves an instance of the service client.\n     * \n     * @param routeParams If a custom `routeResolver` is provided when initiating\n     *  the load balancer, this argument will be passed to the function for route\n     *  calculation, otherwise, it has no effect.\n     * @returns \n     */\n    getInstance(routeParams?: P): ServiceClient\u003cT\u003e;\n\n    /** Closes all the connection. */\n    close(): void;\n}\n\nexport type ChainingProxyInterface = ServiceClient\u003cany\u003e | {\n    [nsp: string]: ChainingProxyInterface;\n};\n\nexport declare class ConnectionManager {\n    register(target: ServiceClient\u003cany\u003e | LoadBalancer\u003cany\u003e): boolean;\n\n    /**\n     * @param target If the target is a string, it is the full name of the\n     *  service (includes the package name, concatenated with `.`).\n     */\n    deregister(target: string | ServiceClient\u003cany\u003e | LoadBalancer\u003cany\u003e, closeConnection?: boolean): boolean;\n\n    /**\n     * @param target If the target is a string, it is the full name of the\n     *  service (includes the package name, concatenated with `.`).\n     * @param routeParams If a custom `routeResolver` is provided when initiating\n     *  the load balancer, this argument will be passed to the function for route\n     *  calculation, otherwise, it has no effect.\n     * @throws If the target service is not registered, a ReferenceError will be\n     *  thrown.\n     */\n    getInstanceOf\u003cT extends object\u003e(\n        target: string | ServiceClient\u003cT\u003e | LoadBalancer\u003cT\u003e\n    ): ServiceClient\u003cT\u003e;\n    getInstanceOf\u003cT extends object, P extends any = any\u003e(\n        target: string | ServiceClient\u003cT\u003e | LoadBalancer\u003cT\u003e,\n        routeParams: P\n    ): ServiceClient\u003cT\u003e;\n\n    /** Closes all the connections of all proxies. */\n    close(): void;\n\n    /**\n     * Instead of calling `#getInstanceOf()` to retrieve the service client,\n     * this function allows us to use chaining syntax to dynamically generated\n     * namespaces and client constructors that can be used as a syntax sugar.\n     * @example\n     *  // Instead of doing this:\n     *  const ins = manager.getInstanceOf\u003cGreeter\u003e(\"examples.Greeter\");\n     *  const result = await ins.sayHello({ name: \"World\" });\n     * \n     *  // We do this:\n     *  const services = manager.useChainingSyntax();\n     *  const result = await services.examples.Greeter.sayHello({ name: \"World\" });\n     * @param rootNsp If set, the namespace will start from the given name.\n     *  Usually leave blank or set to the package name in the proto file.\n     * @example\n     *  const examples = manager.useChainingSyntax(\"examples\");\n     *  const result = await examples.Greeter.sayHello({ name: \"World\" });\n     */\n    useChainingSyntax(rootNsp?: string): ChainingProxyInterface;\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayonli%2Fgrpc-async","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fayonli%2Fgrpc-async","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayonli%2Fgrpc-async/lists"}