{"id":15835089,"url":"https://github.com/kaelzhang/gaia","last_synced_at":"2026-05-19T13:02:05.611Z","repository":{"id":57154559,"uuid":"137333101","full_name":"kaelzhang/gaia","owner":"kaelzhang","description":"Gaia, the framework to make gRPC services","archived":false,"fork":false,"pushed_at":"2020-10-03T09:52:02.000Z","size":174,"stargazers_count":3,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-03T16:15:57.470Z","etag":null,"topics":["grpc","nodejs","server"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/kaelzhang.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}},"created_at":"2018-06-14T08:59:20.000Z","updated_at":"2021-05-24T15:24:52.000Z","dependencies_parsed_at":"2022-09-06T11:20:08.082Z","dependency_job_id":null,"html_url":"https://github.com/kaelzhang/gaia","commit_stats":null,"previous_names":["kaelzhang/gaea"],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fgaia","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fgaia/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fgaia/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaelzhang%2Fgaia/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kaelzhang","download_url":"https://codeload.github.com/kaelzhang/gaia/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242724347,"owners_count":20175185,"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":["grpc","nodejs","server"],"created_at":"2024-10-05T14:20:27.040Z","updated_at":"2026-05-19T13:02:00.569Z","avatar_url":"https://github.com/kaelzhang.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/kaelzhang/gaia.svg?branch=master)](https://travis-ci.org/kaelzhang/gaia)\n[![Coverage](https://codecov.io/gh/kaelzhang/gaia/branch/master/graph/badge.svg)](https://codecov.io/gh/kaelzhang/gaia)\n\n# gaia (for node.js)\n\nGaia, the very framework to make [gRPC](https://grpc.io) services. Gaia defines a definitely intuitive way to write gRPC services.\n\n- **Handle Custom Errors** `gRPC` does NOT provide an formal way to handle errors, even lack of documentation, while `gaia` will do it for you.\n- **Manage `.proto` files** `gaia` allows us to share proto files between server and clients. `gaia` shares `gPRC` protobuf files by wrapping them into an npm package and publishing the npm tarball to npm registry.\n- **Eggjs compatible plugins** `gaia` supports to use [egg plugins](https://github.com/search?q=topic%3Aegg-plugin\u0026type=Repositories) to extend your applications.\n- **Restful API service made easy** `gaia` provides a convenient way to define restful API routings upon the existing gRPC services.\n\n`gaia` supports both [**proto2**](https://developers.google.com/protocol-buffers/docs/proto) and [**proto3**](https://developers.google.com/protocol-buffers/docs/proto3).\n\n## Install\n\n```sh\n$ npm i gaia\n```\n\n## Table of Contents\n\n- [APIs](#apis)\n  - [Client](#new-clientroot-clientconfig)\n  - [Server](#new-serverroot-serverconfig)\n- [How gaia makes `.proto` files sharable and portable?](#how-gaia-makes-proto-files-sharable-and-portable)\n  - [Create the client of `hello`](#create-the-client-of-hello)\n  - [Import `.proto` files from `hello`](#import-proto-files-from-hello)\n  - [More about `includeDirs`](#more-about-includedirs)\n- [How to Write a `gaia` Server](#how-to-write-a-gaia-server)\n  - [Packages and name resolution](#packages-and-name-resolution)\n  - [`this` object of the controller methods](#this-object-of-the-controller-methods)\n    - [Reusing other controllers](#reusing-other-controllers)\n    - [Using external services](#using-external-services)\n    - [Using plugins](#using-plugins)\n\n## Synopsis\n\n```js\nconst {\n  Server,\n  Client,\n  resolvePackage\n} = require('gaia')\n\nconst root = path.join(__dirname, 'example', 'hello')\n```\n\nTo make better understanding the usage of `gaia`, **the example below is based on the demo in the\n[`example/node/hello`](https://github.com/kaelzhang/gaia/tree/master/example/node/hello) directory**.\n\nStart server:\n\n```js\nnew Server(root).listen(50051)\n```\n\nRun client:\n\n```js\nconst {\n  // service Greeter\n  Greeter\n} = new Client(root).connect('localhost:50051')\n\nconst run = async () =\u003e {\n  const {message} = await Greeter.sayHello({name: 'world'})\n\n  console.log(message)\n}\n\nrun()\n// Hello world\n```\n\n# APIs\n\n## new Client(root)\n\nCreates the gaia client.\n\n- **root** `path` the root path to load the client from\n\n### client.connect(host):\n\nConnects to the gRPC server and returns the service methods\n\n- **host** `string` the server host to connect to which includes the server hostname and port and whose pattern is `\u003chostname\u003e:\u003cport\u003e`\n\n## new Server(root, serverConfig?)\n\n- **root** `path` the root path to load the server from\n- **serverConfig?** `ServerConfig={}` server configurations\n\n```ts\ninterface ServerConfig {\n  // Defines where to load controllers\n  controller_root?: string = 'controller'\n  plugins?: Array\u003cPlugin\u003e\n  services?: Services\n}\n\ninterface Package {\n  // The root path of the package\n  path?: string\n  // The package name of the package\n  package?: string\n\n  // Either path or package should be defined.\n}\n\ninterface Plugin extends Package {\n  // Configurations for the plugin\n  config: object\n}\n\ninterface Service extends Package {\n  // the host param of `client.connect(host)`\n  host: string\n}\n\ninterface Services {\n  [name: string]: Service\n}\n```\n\n### server.listen(port): this\n\n- **port** `number` the port which gRPC server will listen to.\n\nStart the gaia server.\n\n### server.kill()\n\nForcibly shut down the gRPC server\n\n### await server.close()\n\nGracefully shut down the server\n\n## resolvePackage(id: string): string\n\n- **id** `string` package id\n\nReturns the root path of the package\n\n```js\nnew Client(resolvePackage('foo')).connect(host)\n```\n\n## How `gaia` makes `.proto` files sharable and portable?\n\n`gaia` takes full advantage of npm packages to share proto files.\n\nA minimun `gaia` service portable, as well as service `hello` or package `hello`, could be:\n\n```\n/path/to/hello/\n  |-- proto/\n  |       |-- hello.proto\n  |-- package.json\n```\n\nAnd in `proto/hello.proto`:\n\n```protobuf\nsyntax = \"proto3\";\n\nservice Greeter {\n  rpc SayHello (HelloRequest) returns (HelloReply) {}\n}\n\nmessage HelloRequest {\n  string name = 1;\n}\n\nmessage HelloReply {\n  string message = 1;\n}\n```\n\npackage.json\n\n```js\n{\n  \"name\": \"hello\",\n  \"gaia\": {\n    ...\n  }\n}\n```\n\nThe the **optional** field `\"gaia\"` of package.json follows the schema:\n\n```ts\ninterface FieldGaia {\n  // Tells `gaia` which properties of error should be\n  // - collected, serialized and transmitted to the clients.\n  // - or deseriialized from server\n  // `errorProps` defaults to `['code', 'message']`\n\n  // if the server throws an `error`, by default, gaia will collect\n  // - `error.code`,\n  // - `error.message`\n  // and send them to its clients, while other properties will be omitted.\n  errorProps?: Array\u003cstring\u003e = ['code', 'message']\n  // Specifies where to load proto files.\n  // `protoPath` should be a relative path to `root`\n  protoPath?: string = 'proto'\n  // Proto filenames inside `protoPath`.\n  // If not specified, gaia will search all `*.proto` files inside `protoPath`.\n  protos?: Array\u003cstring\u003e | string = '*.proto'\n\n  // See section #import-proto-files-from-hello below\n  protoDependencies?: Array\u003cstring\u003e = []\n}\n```\n\nApparently, package `hello` has everything we need to create a client agent for service `hello`.\n\nAnd package `hello` is language-independent which only contains proto files and client configurations.\n\n### Create the client of `hello`\n\nAssume that we have a new project `foo`, and we `npm install hello`.\n\n```\n/path/to/foo/\n  |-- proto/\n  |        |-- foo.proto\n  |-- node_modules/\n  |              |-- hello/\n  |-- package.json\n```\n\nThen if the `hello` service is already running on port `8000`, we could create a hello client by following lines:\n\n```js\nconst {Client} = require('gaia')\nconst {Greeter} = new Client('/path/to/foo/node_modules/hello').connect('localhost:8000')\n```\n\n### Import `.proto` files from `hello`\n\nSince project `foo`, as we introduced above, has a dependency `hello`, we could import `.proto` files from package `hello`.\n\nin `/path/to/foo/proto/foo.proto`:\n\n```protobuf\nsyntax = \"proto3\";\n\n// We could install a package and import things from it\n// as well as we do in JavaScript es modules. Oh yeah! 😆\nimport \"hello/proto/hello.proto\"\n\nservice FooGreeter {\n  // We could reuse message types from package `hello`\n  rpc SayHello (HelloRequest) returns (HelloReply) {}\n}\n```\n\nIn order to do that, we need to declare that `hello` is a `gaia` dependency of `foo` by adding some fields in package.json:\n\n```js\n{\n  \"name\": \"foo\",\n  \"gaia\": {\n    // So that we could import .proto files from package `hello`\n    \"protoDependencies\": [\n      // We have to add \"hello\" here.\n      \"hello\"\n    ]\n  },\n  \"dependencies\": {\n    // This is generated by `npm install`\n    \"hello\": \"^1.0.0\"\n  }\n}\n```\n\nAnd `gaia` will manage the [`--proto_path`](https://developers.google.com/protocol-buffers/docs/proto3#importing-definitions)s ([includeDirs](https://www.npmjs.com/package/@grpc/proto-loader)) for you, so that gRPC Protobuf Loader will know where to search and import `.proto` files\n\n### More about `includeDirs`\n\n`gaia` recursively parses the `protoDependencies` of project `foo`, and its `protoDependency`'s `protoDependencies` to generate the `options.includeDirs` option for [`@grpc/proto-loader`](https://www.npmjs.com/package/@grpc/proto-loader)\n\n## How to Write a `gaia` Server\n\nTake the project `hello` which introduced above for example.\n\nSince we define a `Greeter` service in `hello.proto`, we must implement the corresponding controller by ourselves.\n\nService controllers should be defined in directory `/path/to/hello/controller` which can be changed with by config `controller_root`.\n\nWe must provide a `Greeter.js` in that directory.\n\n```\n/path/to/hello/\n  |-- controller/\n  |            |-- Greeter.js\n```\n\nin [`Greeter.js`](example/hello/controller/Greeter.js), there should be an async/sync method named `SayHello` in `exports` because we defined a `SayHello` rpc method in service `Greeter`\n\n```js\nexports.sayHello = ({name}) =\u003e ({\n  message: `Hello ${name}`\n})\n```\n\n### Packages and name resolution\n\nFirst the innermost package scope is searched, then the next-innermost, and so on, and at last the service name.\n\nAssume that we have the following protocol buffer.\n\n```proto\npackage foo.bar;\n\nservice Baz {\n  rpc Quux (Req) returns (Res) {}\n}\n```\n\nThen in directory `controller_root`, we need to create a JavaScript file `foo/bar/Baz.js` whose `exports` has a `Quux` method.\n\n### `this` object of the controller methods\n\nThere are several properties could be access by `this` object of the controller methods.\n\n#### Reusing other controllers\n\nWe could access other controller methods by\n\n```js\nthis.controller[namespace0][namespace1]...[serviceName][methodName]\n```\n\nFor example, we could access the `Quux` method by\n\n```js\nexports.OtherMethodsOfSomeService = async function (request) {\n  const data = await this.controller.foo.bar.Baz.Quux(request)\n  // ...\n  return something\n}\n```\n\n#### Using external services\n\nIf we provide `serverConfig.services` for server\n\n```js\nnew Server('/path/to/service/foo', {\n  ...otherConfig,\n  services: {\n    hello: {\n      // 'hello' is a gaia server\n      package: 'hello'\n    }\n  }\n})\n.listen(port)\n```\n\nThen, client of the service `'hello'` could be accessed from the service controller of foo by:\n\n```js\nexports.Quux = async function ({name}) {\n  const {message} = await this.service.hello.SayHello({name})\n  return {\n    property: message\n  }\n}\n```\n\n#### Using plugins\n\n\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaelzhang%2Fgaia","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkaelzhang%2Fgaia","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaelzhang%2Fgaia/lists"}