{"id":13808727,"url":"https://github.com/smnbbrv/ngx-grpc","last_synced_at":"2025-04-06T08:14:35.868Z","repository":{"id":42997196,"uuid":"221891084","full_name":"smnbbrv/ngx-grpc","owner":"smnbbrv","description":"Angular gRPC framework","archived":false,"fork":false,"pushed_at":"2024-08-21T09:18:39.000Z","size":2961,"stargazers_count":238,"open_issues_count":19,"forks_count":34,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-10-16T09:01:39.743Z","etag":null,"topics":[],"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/smnbbrv.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","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":"2019-11-15T09:33:28.000Z","updated_at":"2024-09-14T14:38:35.000Z","dependencies_parsed_at":"2024-08-26T11:55:28.558Z","dependency_job_id":"aa41d34e-d99d-4b80-830c-ca484a266225","html_url":"https://github.com/smnbbrv/ngx-grpc","commit_stats":{"total_commits":214,"total_committers":17,"mean_commits":"12.588235294117647","dds":"0.37850467289719625","last_synced_commit":"2e1a1c6383e5f146c0e54dfd999f2b930b2aac2a"},"previous_names":["smnbbrv/ngx-grpc","ngx-grpc/ngx-grpc"],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnbbrv%2Fngx-grpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnbbrv%2Fngx-grpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnbbrv%2Fngx-grpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smnbbrv%2Fngx-grpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smnbbrv","download_url":"https://codeload.github.com/smnbbrv/ngx-grpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247261590,"owners_count":20910107,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-08-04T01:01:50.561Z","updated_at":"2025-04-06T08:14:35.850Z","avatar_url":"https://github.com/smnbbrv.png","language":"TypeScript","funding_links":[],"categories":["Table of contents"],"sub_categories":["Third Party Components"],"readme":"# ngx-grpc\n\nAngular gRPC framework.\n\n![Workflow status](https://img.shields.io/github/workflow/status/ngx-grpc/ngx-grpc/Push) ![Npm version](https://img.shields.io/npm/v/@ngx-grpc/core)\n\n[Changelog](https://github.com/ngx-grpc/ngx-grpc/blob/master/CHANGELOG.md)\n\n## Features\n\n- two-way-binding-friendly protobuf messages implementation (instead of Java-like setters / getters / builder pattern in original google-protobuf)\n- client services are wired to Angular's dependency injection\n- typescript first-class support\n- rxjs first-class support\n- client \u0026 bidirectional streaming (only possible with @improbable-eng/grpc-web)\n- interceptors\n- logger\n- support for well-known types, including `Any`\n- support for [JSON mappings](https://developers.google.com/protocol-buffers/docs/proto3#json)\n- 3 different client implementations: grpc-web, @improbable-eng/grpc-web and web worker\n- easy to install, update and support thanks to npm packages\n\n## Example\n\n\u003e The example requires docker \u0026 docker-compose to be installed\n\nClone this repository and run\n\n```\nnpm ci\nnpm run build\nnpm run examples\n```\n\nOn m1 chips replace `npm ci` with `npm ci --target_arch=x64 --no-optional`.\n\nNow open your browser at [http://localhost:4200/](http://localhost:4200/). The source code could be found at [examples](examples) directory.\n\n## Installation\n\n\u003e The documentation uses @ngx-grpc/grpc-web-client by default, however is applicable to any client you choose.\n\nFirst ensure that you\n\n- installed `protoc` [as a binary](https://github.com/protocolbuffers/protobuf#protocol-compiler-installation) or as a part of `grpc-tools` npm package\n- configured your backend / grpc-web proxy according to [grpc-web documentation](https://github.com/grpc/grpc-web) or follow documentation of the alternative client if you use one\n\nThen in your Angular project's root directory run\n\n```sh\nnpm i -S @ngx-grpc/common @ngx-grpc/core @ngx-grpc/grpc-web-client @ngx-grpc/well-known-types google-protobuf grpc-web\nnpm i -D @ngx-grpc/protoc-gen-ng @types/google-protobuf\n```\n\nWhere:\n\n- [@ngx-grpc/common](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/common) contains common reusable types for other ngx-grpc packages\n- [@ngx-grpc/core](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/core) contains angular specific implementation\n- [@ngx-grpc/grpc-web-client](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/grpc-web-client) provides a client based on [grpc-web](https://github.com/grpc/grpc-web)\n- [@ngx-grpc/protoc-gen-ng](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/protoc-gen-ng) generates the code based on your proto files\n- [@ngx-grpc/well-known-types](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/well-known-types) contains well-known types\n- [google-protobuf](https://github.com/protocolbuffers/protobuf/tree/master/js) is required to encode / decode the messages\n- [grpc-web](https://github.com/grpc/grpc-web) implements the transport between the browser and grpc proxy\n\nAlso you can choose between alternative client implementations:\n\n- [@ngx-grpc/grpc-web-client](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/grpc-web-client) - based on [grpc-web](https://github.com/grpc/grpc-web)\n- [@ngx-grpc/improbable-eng-grpc-web-client](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/improbable-eng-grpc-web-client) - alternative client implementation based on [@improbable-eng/grpc-web](https://github.com/improbable-eng/grpc-web)\n- [@ngx-grpc/worker-client](https://github.com/ngx-grpc/ngx-grpc/tree/master/packages/worker-client) - similar to @ngx-grpc/grpc-web-client but running in worker\n\n## Generate the code\n\n### MacOS / Linux\n\nAdd `proto:generate` script to your `package.json` `scripts` section:\n\n```json\n{\n  \"scripts\": {\n    \"proto:generate\": \"protoc --plugin=protoc-gen-ng=$(which protoc-gen-ng) --ng_out=\u003cOUTPUT_PATH\u003e -I \u003cPROTO_DIR_PATH\u003e \u003cPROTO_FILES\u003e\"\n  }\n}\n```\n\nWhere:\n\n- `OUTPUT_PATH` - the directory your code will be generated at (please ensure the directory exists, otherwise you'll have a `protoc` error)\n- `PROTO_DIR_PATH` - the root path of your proto files\n- `PROTO_FILES` - list of proto files to use\n\nExample:\n\n```json\n{\n  \"scripts\": {\n    \"proto:generate\": \"protoc --plugin=protoc-gen-ng=$(which protoc-gen-ng) --ng_out=./src/proto -I ../proto $(find ../proto -iname \\\"*.proto\\\")\"\n  }\n}\n```\n\nFinally, run `npm run proto:generate` every time you want to (re)generate the code\n\n### Advanced generator config\n\nYou can have more control on what and how is being generated. Create a `ngx-grpc.conf.js` (`.json` format also supported) file in your project root.\n\nE.g. to generate well-known types in your project instead of using `@ngx-grpc/well-known-types`, use this config:\n\n```js\nmodule.exports = {\n  embedWellKnownTypes: true,\n};\n```\n\nMore details on the configuration properties and their default values see [here](https://github.com/ngx-grpc/ngx-grpc/blob/master/packages/protoc-gen-ng/src/config.ts).\n\nThen update your package.json command with path to this file `config=./ngx-grpc.conf.js`:\n\n```json\n{\n  \"scripts\": {\n    \"proto:generate\": \"protoc --plugin=protoc-gen-ng=$(which protoc-gen-ng) --ng_out=config=./ngx-grpc.conf.js:./src/proto -I ../proto $(find ../proto -iname \\\"*.proto\\\")\"\n  }\n}\n```\n\n### Windows\n\nUnfortunately the way to generate files on Windows slightly differs. Here is a sophisticated example that shows how to scan windows folder with proto files and pass it to protoc-gen-ng.\n\n```json\n{\n  \"scripts\": {\n    \"proto:generate:win\": \"for /f %G in ('dir /b ..\\\\proto\\\\*.proto') do grpc_tools_node_protoc --plugin=protoc-gen-ng=.\\\\node_modules\\\\.bin\\\\protoc-gen-ng.cmd --ng_out=.\\\\output\\\\path -I ..\\\\proto ..\\\\proto\\\\%G\",\n  }\n}\n```\n\n## Usage\n\n### Import the required modules\n\nIn general, you need to import at least\n\n- GrpcCoreModule\n- GrpcWebClientModule (or another client implementation, see below)\n\n```ts\n@NgModule({\n  imports: [\n    GrpcCoreModule.forRoot(),\n    GrpcWebClientModule.forRoot({\n      settings: { host: 'http://localhost:8080' },\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\nYou also can define them in child modules by using `forChild()` methods instead of `forRoot()`.\n\n### Per-service clients configuration\n\nInstead of configuring the client settings globally you can configure them per-service. Every service has an injected configuration which could be found e.g. in the corresponding `*.pbconf.ts` file.\n\nE.g. for a service `TestServiceClient` you need to provide the `GRPC_TEST_SERVICE_CLIENT_SETTINGS`:\n\n```ts\n@NgModule({\n  providers: [\n    { provide: GRPC_TEST_SERVICE_CLIENT_SETTINGS, useValue: { host: 'http://localhost:8080' } as GrpcWebClientSettings },\n  ],\n})\nexport class AppModule {}\n```\n\nTo set grpcweb / binary proto format use\n\n```ts\n{ provide: GRPC_TEST_SERVICE_CLIENT_SETTINGS, useValue: { host: 'http://localhost:8080', format: 'binary' } as GrpcWebClientSettings },\n```\n\nFrom now on this particular service is set.\n\n### Service client methods\n\n#### Concept\n\nEvery client call accepts a message to be sent and returns an `Observable` of response message(s). However, the request is not being executed until the `Observable` gets subscribed, so it can be safely used at any place.\n\nTo cancel the request / close the connection simply unsubscribe from that `Subscription`. Of course, if connection is closed by the server or the error happens, the `Observable` gets terminated.\n\n```ts\nclass MyService {\n\n  constructor(private client: EchoClient) {}\n\n  sendOne() {\n    this.client.echo(new EchoRequest({ message: 'text' })).subscribe(res =\u003e console.log(res));\n    \n    // or if you want to terminate it, e.g. it is a server stream or you navigate away and do not need to wait\n    const sub = this.client.echo(new EchoRequest({ message: 'text' })).subscribe(res =\u003e console.log(res));\n\n    setTimeout(() =\u003e sub.unsubscribe(), 1000); // this closes connection\n  }\n\n}\n```\n\nThe behaviour above is possible due to the `Observable`'s natural laziness and ability to be terminated.\n\n**The server streaming** has the same signature as **the unary requests**, but the returned `Observable` can emit more than one message and the connection is kept open.\n\n**The client streaming** is differrent, because it accepts an `Observable` (and any of its derivatives, such as `Subject`, `BehaviourSubject`, etc.) of messages \n\n\n```ts\nclass MyService {\n\n  constructor(private client: EchoClient) {}\n\n  sendMany() {\n    const stream = from(['message 1', 'message 2', 'message 3']);\n\n    this.client.echoMany(stream).subscribe(res =\u003e console.log(res));\n  }\n\n}\n```\n\n**The bidirectional streaming** has the same signature as the client's one and is a combination of server and client streaming.\n\n#### Implementation details\n\nEach RPC has two corresponding methods.\n\n- the first, that emits messages, is a direct method of the service client.\n- the second, that emits events, is scoped into service client property `$raw`. \n\nE.g. for `rpc Echo(...)` there would be the following:\n\n- `echo(...)` - returns `Observable` of messages and throws errors in case of non-zero status codes. This is the most common use-case\n- `.$raw.echo(...)` - returns `Observable` of `GrpcEvent`s. Events could be of two kinds: `GrpcDataEvent` containing the message inside and `GrpcStatusEvent` containing gRPC status response. Apart from the returned data type there is important difference in the behaviour. There are no errors thrown in this stream (by design). All errors are considered to be normal `GrpcStatusEvent`s. Furthermore, this method is the only one where it is anyhow possible to read the gRPC status code `0` (`OK`) metadata. This method is not that comfortable to use in every place, but it can do things that are not achievable with the method above.\n\nThere are two custom RxJS operators that could be used on the stream to make it easier:\n\n- `throwStatusErrors` - searches for the non-zero status codes and throws them as errors\n- `takeMessages` - searches for the messages\n\nFor usage example look at any of your generated `.pbsc.ts` file. In fact, those two operators turn the raw method into a 'normal' one.\n\n### Messages\n\nTo create a new message just pass its initial values to the constructor: `new Message(myInitialValues)`. Here is some information on the message's methods:\n\n- `constructor` - accepts a message or an object with initial values. All values are safely / deeply cloned.\n- `toObject()` - casts message *as is* to a normal JavaScript object\n- `toJSON()` - convenience method to be able to pass message to `JSON.stringify(msg)`\n- `toProtobufJSON()` - constructs a [protobuf-defined JSON](https://developers.google.com/protocol-buffers/docs/proto3#json). Accepts an optional `GrpcMessagePool` (see `google.protobuf.Any` section) which is required only if the message or some of its descendants embeds `google.protobuf.Any`\n\n#### Int64\n\nJavaScript does not support `int64`, out of the box, that's why all of its kinds are generated as string by default.\n\nYou can however override this behavior by passing `JS_NUMBER` or `JS_STRING` option to the appropriate field. Example:\n\n```protobuf\nmessage Message { \n  int64 bigInt = 1 [jstype = JS_NUMBER];\n  uint64 bigUint = 2 [jstype = JS_NUMBER];\n}\n```\n\n\n### Well-known types\n\nThe well-known types are served as a separate package. You can also configure generation of the well-known types together with your proto definitions (like older versions did).\n\nSome types have additional functionality, see below.\n\n#### google.protobuf.Any\n\nThe `google.protobuf.Any` has additional methods `pack` and `unpack`.\n\nUnpacking the message requires a special message pool `GrpcMessagePool` where the expected message types are listed; otherwise the unpacking would not be possible.\n\nExample of type-safe unpacking:\n\n```ts\n// we expect one of 3 message types to be packed into Any\nconst myAny: Any;\nconst pool = new GrpcMessagePool([Empty, Timestamp, MyMessage]);\n\ntry {\n  switch(myAny.getPackedMessageType(pool)) {\n    case Empty: console.log('Empty found', myAny.unpack\u003cEmpty\u003e(pool)); break;\n    case Timestamp: console.log('Timestamp found', myAny.unpack\u003cTimestamp\u003e(pool)); break;\n    case MyMessage: console.log('MyMessage found', myAny.unpack\u003cMyMessage\u003e(pool)); break;\n    default: console.log('No packed message inside');\n  }\n} catch (ex) {\n  console.error('Something went wrong, e.g. packed message definition is not in the pool');\n}\n```\n\n#### google.protobuf.Timestamp\n\nThe `google.protobuf.Timestamp` has additional methods to cast from / to `Date` and ISO string date representation.\n\n### Custom well-known types\n\nFor well-known types that are not part of the `google.protobuf` package, you can override the imports to use for specific packages.  \n\nThis is especially useful if you are using a protobuf schema registry like [Buf](https://buf.build/) for sharing some common messages through different projects.\n\n\nExample of a custom well-known type configuration:\n```js\nmodule.exports = {\n  customWellKnownTypes: {\n    \"company.internal.commons\": \"@company-internal/grpc-commons\"\n  }\n}\n```\nThis will change all import statements that reference a message of the package `company.internal.commons` to use `@company-internal/grpc-commons`, instead of the relative file path.\n\nIf the `embedWellKnownTypes` configuration is enabled, the `customWellKnownTypes` configuration will be ignored and the messages will be generated as usual.\n\n### Interceptors\n\nYou can add global interceptors to all gRPC calls like Angular's built-in `HttpClient` interceptors.\n\nThe important differences\n\n- unlike `HttpClient` interceptors `GrpcInterceptor`s need to work with event streams; there are no errors thrown. Instead you should listen to the `GrpcStatusEvent` and decide whether it is an error or not. Please keep this in mind\n- the incoming data can be a message or a stream of messages (in case of client streaming)\n\nAs an example see `GrpcLoggerInterceptor` [in the core package](packages/core/src/lib/grpc-logger-interceptor.ts).\n\n### Logger\n\nYou can enable logging using `GrpcLoggerInterceptor` (provided by @ngx-grpc/core). Add to your `AppModule` the following import:\n\n```ts\nGrpcLoggerModule.forRoot(),\n```\n\nThen open the browser console and you should see all the requests and responses in a readable format.\n\nOptionally, you can provide a more detailed configuration. Example:\n\n```ts\nGrpcLoggerModule.forRoot({ \n  settings: { \n     // enables logger in dev mode and still lets you see them in production when running `localStorage.setItem('logger', 'true') in the console`\n    enabled: localStorage.getItem('logger') === 'true' || !environment.production,\n     // protobuf json is more human-readable than the default toObject() mapping\n     // please beware: if you use google.protobuf.Any you must pass the proper `messagePool` argument\n    requestMapper: (msg: GrpcMessage) =\u003e msg.toProtobufJSON(),\n    responseMapper: (msg: GrpcMessage) =\u003e msg.toProtobufJSON(),\n  },\n}),\n```\n\n## Alternative client: @improbable-eng/grpc-web\n\nThe alternative grpc-web implementation from [Improbable Engineering](https://github.com/improbable-eng) provides way more features than standard grpc-web from Google. It supports [various transports](https://github.com/improbable-eng/grpc-web/blob/master/client/grpc-web/docs/transport.md) including WebSocket-based and even Node (can be useful e.g. for SSR).\n\n**The only** client that supports client / bidirectional streaming. This however also requires the server to be able to handle websocket transport. For this purpose improbable-eng team introduced [grpc-web-proxy](https://github.com/improbable-eng/grpc-web/blob/master/go/grpcwebproxy/README.md) - a special facade for the normal grpc server that acts like envoy, but has also the ability to handle websocket transport.\n\nInstallation:\n\n```sh\nnpm i -S @ngx-grpc/improbable-eng-grpc-web-client @improbable-eng/grpc-web\n```\n\nThen configuration is similar to the other clients, however there is a transport to configure:\n\n```ts\nimport { grpc } from '@improbable-eng/grpc-web';\nimport { GrpcCoreModule } from '@ngx-grpc/core';\nimport { ImprobableEngGrpcWebClientModule } from '@ngx-grpc/improbable-eng-grpc-web-client';\n\nconst xhr = grpc.CrossBrowserHttpTransport({});\nconst ws = grpc.WebsocketTransport();\n\n@NgModule({\n  imports: [\n    GrpcCoreModule.forRoot(),\n    ImprobableEngGrpcWebClientModule.forChild({\n      settings: {\n        host: 'http://localhost:8080',\n        // we might want to use different transports as recommended by improbable-eng team\n        // because websocket transport acts a bit differently and is intended for client streaming only\n        transport: {\n          unary: xhr,\n          serverStream: xhr,\n          clientStream: ws,\n          bidiStream: ws,\n        },\n        // or simply e.g.\n        // transport: ws, // to configure all methods to use websockets\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n## Web worker\n\nWeb worker allows to run gRPC clients, messages serialization and deserialization in a separate thread. It might give some performance benefits on large data sets; however the main reason of the worker is to avoid blocking the main thread. That means that rendering engine has more resources to work on rendering while the messages processing is done in parallel.\n\nFirst, install additional packages:\n\n```sh\nnpm i -S @ngx-grpc/worker @ngx-grpc/worker-client\n```\n\nThen configure the web worker. First you need to adapt the code generation settings (see above) to generate `pbwsc` files. These files will contain the worker service client definitions.\n\nNow, generate the worker (angular cli), e.g. with the name `grpc`:\n\n```sh\nng g web-worker grpc\n```\n\nor for Angular \u003c 9\n\n```sh\nng g worker grpc\n```\n\nYou should see `grpc.worker.ts` close to your `app.module.ts`. Open this file and replace the contents with the following:\n\n```ts\n/// \u003creference lib=\"webworker\" /\u003e\n\nimport { GrpcWorker } from '@ngx-grpc/worker';\nimport { GrpcWorkerEchoServiceClientDef } from '../proto/echo.pbwsc';\n\nconst worker = new GrpcWorker();\n\nworker.register(\n  // register here all the service clients definitions\n  GrpcWorkerEchoServiceClientDef,\n);\n\nworker.start();\n```\n\nFinally use the following imports:\n\n```ts\n@NgModule({\n  imports: [\n    GrpcCoreModule.forRoot(),\n    GrpcWorkerClientModule.forRoot({\n      worker: new Worker('./grpc.worker', { type: 'module' }),\n      settings: { host: 'http://localhost:8080' },\n    }),\n  ],\n})\nexport class AppModule {\n}\n```\n\nThat's it. All your requests are served by worker.\n\n## Not implemented (yet)\n\n- [Proto 2 Extensions](https://developers.google.com/protocol-buffers/docs/proto#extensions)\n\n## Contributing\n\n- to run tests on Apple m1 chips use `npm ci --no-optional` and `brew install protoc-gen-grpc-web`\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmnbbrv%2Fngx-grpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmnbbrv%2Fngx-grpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmnbbrv%2Fngx-grpc/lists"}