Skip to content

Commit

Permalink
feat: generating new clients implementing GenericClient on generate c…
Browse files Browse the repository at this point in the history
…ommand
  • Loading branch information
lewnelson committed Jan 26, 2023
1 parent 8deb5ad commit 2478a44
Show file tree
Hide file tree
Showing 24 changed files with 770 additions and 144 deletions.
34 changes: 5 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ client.getName(new GetNameRequest(), (error: Error | null, response: GetNameResp
to this:

```typescript
const response = await client.unaryRequest("getName", new GetNameRequest());
const response = await client.getName(new GetNameRequest());
```

## Install
Expand Down Expand Up @@ -50,41 +50,17 @@ The generator uses [grpc-tools](https://github.com/grpc/grpc-node/tree/master/pa

```typescript
import * as grpc from "@grpc/grpc-js";
import { createClient } from "@lewnelson/grpc-ts";
import { GeneratedClient } from "./path/to/output/generated/generated_grpc_pb";
import { GeneratedClient } from "./path/to/output/generated/client";

// Creating a client with insecure credentials
const client = createClient(GeneratedClient, "localhost:50051");
const client = new GeneratedClient("localhost:50051");

// Creating a client with SSL credentials
const client = createClient(
GeneratedClient,
const client = new GeneratedClient(
"localhost:50051",
{ credentials: grpc.credentials.createSsl() }
);

```

### Making unary requests

All unary requests are available on `client.unaryRequest`. The first argument is the name of the method to call. Only methods which return a `grpc.ClientUnaryCall` are available on `client.unaryRequest`. The second argument is the request object. This maps to the method name.

See [examples/unaryRequests.ts](./examples/unaryRequests.ts) for examples.

### Making server streaming requests

All server streaming requests are available on `client.serverStreamRequest`. The first argument is the name of the method to call. Only methods which return a `grpc.ClientReadableStream` are available on `client.serverStreamRequest`. The second argument is the request object. This maps to the method name.

See [examples/serverStreaming.ts](./examples/serverStreaming.ts) for examples.

### Making client streaming requests

All client streaming requests are available on `client.clientStreamRequest`. The first argument is the name of the method to call. Only methods which return a `grpc.ClientWritableStream` are available on `client.clientStreamRequest`. The second argument is the callback when the server makes the unary response.

See [examples/clientStreaming.ts](./examples/clientStreaming.ts) for examples.

### Making bidirectional streaming requests

All bidirectional streaming requests are available on `client.duplexStreamRequest`. The first argument is the name of the method to call. Only methods which return a `grpc.ClientDuplexStream` are available on `client.duplexStreamRequest`. Optionally you can bind a callback to read the server stream when calling the `client.duplexStreamRequest` function.

See [examples/bidirectionalStreaming.ts](./examples/bidirectionalStreaming.ts) for examples.
For full usage see [Examples](./examples/README.md).
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Examples

- [Using the generated client](./generated_client/README.md)
20 changes: 20 additions & 0 deletions examples/generated/example/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This file was generated using @lewnelson/grpc-ts
// Do not modify this file directly

import { createClient, UnaryRequest, ReadableStreamRequest, WriteableStreamRequest, DuplexStreamRequest } from "@lewnelson/grpc-ts";
import { ExampleClient as grpc_ExampleClient } from "./example_grpc_pb";

export interface IExampleClient {
getName: UnaryRequest<grpc_ExampleClient, "getName">;
streamMessages: ReadableStreamRequest<grpc_ExampleClient, "streamMessages">;
emitMessages: WriteableStreamRequest<grpc_ExampleClient, "emitMessages">;
chat: DuplexStreamRequest<grpc_ExampleClient, "chat">;
}

export class ExampleClient implements IExampleClient {
constructor(address: Parameters<typeof createClient>[1], options?: Parameters<typeof createClient>[2]);
getName: UnaryRequest<grpc_ExampleClient, "getName">;
streamMessages: ReadableStreamRequest<grpc_ExampleClient, "streamMessages">;
emitMessages: WriteableStreamRequest<grpc_ExampleClient, "emitMessages">;
chat: DuplexStreamRequest<grpc_ExampleClient, "chat">;
}
31 changes: 31 additions & 0 deletions examples/generated/example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// This file was generated using @lewnelson/grpc-ts
// Do not modify this file directly

const { createClient } = require("@lewnelson/grpc-ts");
const grpc = require("./example_grpc_pb");

class ExampleClient {
constructor() {
this.client = createClient(grpc.ExampleClient, ...arguments);
}

getName() {
return this.client.unaryRequest("getName", ...arguments);
}

streamMessages() {
return this.client.serverStreamRequest("streamMessages", ...arguments);
}

emitMessages() {
return this.client.clientStreamRequest("emitMessages", ...arguments);
}

chat() {
return this.client.duplexStreamRequest("chat", ...arguments);
}
}

exports.ExampleClient = ExampleClient;


5 changes: 5 additions & 0 deletions examples/generated_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Generated client examples

The `grpc-ts` CLI tool will generate the output from [grpc-tools](https://www.npmjs.com/package/grpc-tools) as well as creating a client class which uses the [GenericClient](../generic_client/README.md) under the hood to make requests.

The client class method names are a 1:1 mapping of the methods defined in the generated client class on the `_grpc_pb.d.ts` files.
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { createClient } from "@lewnelson/grpc-ts";
import { ExampleClient } from "./generated/example/example_grpc_pb";
import { ChatRequest, ChatResponse } from "./generated/example/example_pb";
import { ExampleClient } from "../generated/example";
import { ChatRequest, ChatResponse } from "../generated/example/example_pb";

// Creates client with insecure credentials
const client = createClient(ExampleClient, "localhost:50051");
const client = new ExampleClient("localhost:50051");

// Basic implementation
(async () => {
const stream = client.duplexStreamRequest("chat", {
const stream = client.chat({
onData: (
complete: boolean,
error: Error | null,
Expand Down Expand Up @@ -48,7 +47,7 @@ const client = createClient(ExampleClient, "localhost:50051");
// Advanced usage
(async () => {
// Create the stream without binding a callback
const stream = client.duplexStreamRequest("chat");
const stream = client.chat();

// Access the underlying grpc-js Duplex stream
stream._stream.on("data", (chunk: unknown) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { credentials, Metadata, ServiceError } from "@grpc/grpc-js";
import { createClient } from "@lewnelson/grpc-ts";
import { ExampleClient } from "./generated/example/example_grpc_pb";
import { ExampleClient } from "../generated/example";
import {
EmitMessagesRequest,
EmitMessagesResponse,
} from "./generated/example/example_pb";
} from "../generated/example/example_pb";

// Creates client with insecure credentials
const client = createClient(ExampleClient, "localhost:50051");
const client = new ExampleClient("localhost:50051");

// Making a basic request
(async () => {
const stream = client.clientStreamRequest(
"emitMessages",
const stream = client.emitMessages(
(error: ServiceError | null, response?: EmitMessagesResponse) => {
if (error) {
// Server responded with an error
Expand All @@ -39,8 +37,7 @@ const client = createClient(ExampleClient, "localhost:50051");

// Advanced usage
(async () => {
const stream = client.clientStreamRequest(
"emitMessages",
const stream = client.emitMessages(
(error: ServiceError | null, response?: EmitMessagesResponse) => {
if (error) {
// Server responded with an error
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Metadata } from "@grpc/grpc-js";
import { createClient } from "@lewnelson/grpc-ts";
import { ExampleClient } from "./generated/example/example_grpc_pb";
import { ExampleClient } from "../generated/example";
import {
StreamMessagesRequest,
StreamMessagesResponse,
} from "./generated/example/example_pb";
} from "../generated/example/example_pb";

// Creates client with insecure credentials
const client = createClient(ExampleClient, "localhost:50051");
const client = new ExampleClient("localhost:50051");

// Making a basic request
(() => {
Expand All @@ -16,7 +15,7 @@ const client = createClient(ExampleClient, "localhost:50051");
request.setId("id");

// Make the request
client.serverStreamRequest("streamMessages", request, {
client.streamMessages(request, {
onData: (
complete: boolean,
error: Error | null,
Expand Down Expand Up @@ -48,7 +47,7 @@ const client = createClient(ExampleClient, "localhost:50051");
request.setId("id");

// Make the request specifying metadata and call options
const stream = client.serverStreamRequest("streamMessages", request, {
const stream = client.streamMessages(request, {
metadata: new Metadata({ cacheableRequest: true }),
options: { deadline: new Date(Date.now() + 10e3) },
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { createClient } from "@lewnelson/grpc-ts";
import { Metadata, ServiceError } from "@grpc/grpc-js";
import { ExampleClient } from "./generated/example/example_grpc_pb";
import { GetNameRequest } from "./generated/example/example_pb";
import { ExampleClient } from "../generated/example";
import { GetNameRequest } from "../generated/example/example_pb";

// Creates client with insecure credentials
const client = createClient(ExampleClient, "localhost:50051");
const client = new ExampleClient("localhost:50051");

// Making a basic request
(async () => {
Expand All @@ -14,7 +13,7 @@ const client = createClient(ExampleClient, "localhost:50051");

try {
// Make the request
const response = await client.unaryRequest("getName", request);
const response = await client.getName(request);
// Do something with the response
response.getName();
} catch (error) {
Expand All @@ -35,7 +34,7 @@ const client = createClient(ExampleClient, "localhost:50051");

try {
// Make the request
const response = await client.unaryRequest("getName", request, {
const response = await client.getName(request, {
metadata: new Metadata({ cacheableRequest: true }),
options: {
// Specify a deadline on the request 10 seconds from now
Expand Down
11 changes: 11 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
/** @type {import("ts-jest").JestConfigWithTsJest} */
module.exports = {
preset: "ts-jest",
transform: {
"^.+\\.ts$": [
"ts-jest",
{
tsconfig: "<rootDir>/test/tsconfig.json",
},
],
},
testEnvironment: "node",
setupFilesAfterEnv: ["./jest.setup.ts"],
collectCoverage: true,
coverageDirectory: "./coverage",
coveragePathIgnorePatterns: ["/node_modules/", "/generated/"],
moduleNameMapper: {
"^@lewnelson/grpc-ts$": "<rootDir>/src/index.ts",
},
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"clean": "npm run clean:cli && npm run clean:dist",
"clean:cli": "rimraf cli",
"clean:dist": "rimraf dist",
"generate:test": "rimraf test/generated && node ./cli/index.js generate test/protos test/generated",
"generate:test": "rimraf test/generated && node ./cli/index.js generate test/protos/users.proto test/generated",
"lint": "eslint .",
"lint:fix": "npm run lint --fix",
"prepare": "npx install-peers && npx husky install",
Expand Down
Loading

0 comments on commit 2478a44

Please sign in to comment.