Skip to main content

Gaia, the very framework to make gRPC services

Project description

Build Status Coverage

gaia

Gaia, the very framework to make gRPC services. Gaia defines a definitely intuitive way to write gRPC services.

  • Handle Custom Errors gRPC does NOT provide an formal way to handle errors, even lack of documentation, while gaia will do it for you.
  • 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.
  • Eggjs compatible plugins gaia supports to use egg plugins to extend your applications.
  • Restful API service made easy gaia provides a convenient way to define restful API routings upon the existing gRPC services.

gaia supports both proto2 and proto3.

Install

$ npm i gaia

Table of Contents

Synopsis

const {
  Server,
  Client,
  resolvePackage
} = require('gaia')

const root = path.join(__dirname, 'example', 'hello')

To make better understanding the usage of gaia, the example below is based on the demo in the example/hello directory.

Start server:

new Server(root).listen(50051)

Run client:

const {
  // service Greeter
  Greeter
} = new Client(root).connect('localhost:50051')

const run = async () => {
  const {message} = await Greeter.sayHello({name: 'world'})

  console.log(message)
}

run()
// Hello world

APIs

new Client(root)

Creates the gaia client.

  • root path the root path to load the client from

client.connect(host):

Connects to the gRPC server and returns the service methods

  • host string the server host to connect to which includes the server hostname and port and whose pattern is <hostname>:<port>

new Server(root, serverConfig?)

  • root path the root path to load the server from
  • serverConfig? ServerConfig={} server configurations
interface ServerConfig {
  // Defines where to load controllers
  controller_root?: string = 'controller'
  plugins?: Array<Plugin>
  services?: Services
}

interface Package {
  // The root path of the package
  path?: string
  // The package name of the package
  package?: string

  // Either path or package should be defined.
}

interface Plugin extends Package {
  // Configurations for the plugin
  config: object
}

interface Service extends Package {
  // the host param of `client.connect(host)`
  host: string
}

interface Services {
  [name: string]: Service
}

server.listen(port): this

  • port number the port which gRPC server will listen to.

Start the gaia server.

server.kill()

Forcibly shut down the gRPC server

await server.close()

Gracefully shut down the server

resolvePackage(id: string): string

  • id string package id

Returns the root path of the package

new Client(resolvePackage('foo')).connect(host)

How gaia makes .proto files sharable and portable?

gaia takes full advantage of npm packages to share proto files.

A minimun gaia service portable, as well as service hello or package hello, could be:

/path/to/hello/
  |-- proto/
  |       |-- hello.proto
  |-- package.json

And in proto/hello.proto:

syntax = "proto3";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

package.json

{
  "name": "hello",
  "gaia": {
    ...
  }
}

The the optional field "gaia" of package.json follows the schema:

interface FieldGaia {
  // Tells `gaia` which properties of error should be
  // - collected, serialized and transmitted to the clients.
  // - or deseriialized from server
  // `errorProps` defaults to `['code', 'message']`

  // if the server throws an `error`, by default, gaia will collect
  // - `error.code`,
  // - `error.message`
  // and send them to its clients, while other properties will be omitted.
  errorProps?: Array<string> = ['code', 'message']
  // Specifies where to load proto files.
  // `protoPath` should be a relative path to `root`
  protoPath?: string = 'proto'
  // Proto filenames inside `protoPath`.
  // If not specified, gaia will search all `*.proto` files inside `protoPath`.
  protos?: Array<string> | string = '*.proto'

  // See section #import-proto-files-from-hello below
  protoDependencies?: Array<string> = []
}

Apparently, package hello has everything we need to create a client agent for service hello.

And package hello is language-independent which only contains proto files and client configurations.

Create the client of hello

Assume that we have a new project foo, and we npm install hello.

/path/to/foo/
  |-- proto/
  |        |-- foo.proto
  |-- node_modules/
  |              |-- hello/
  |-- package.json

Then if the hello service is already running on port 8000, we could create a hello client by following lines:

const {Client} = require('gaia')
const {Greeter} = new Client('/path/to/foo/node_modules/hello').connect('localhost:8000')

Import .proto files from hello

Since project foo, as we introduced above, has a dependency hello, we could import .proto files from package hello.

in /path/to/foo/proto/foo.proto:

syntax = "proto3";

// We could install a package and import things from it
// as well as we do in JavaScript es modules. Oh yeah! 😆
import "hello/proto/hello.proto"

service FooGreeter {
  // We could reuse message types from package `hello`
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

In order to do that, we need to declare that hello is a gaia dependency of foo by adding some fields in package.json:

{
  "name": "foo",
  "gaia": {
    // So that we could import .proto files from package `hello`
    "protoDependencies": [
      // We have to add "hello" here.
      "hello"
    ]
  },
  "dependencies": {
    // This is generated by `npm install`
    "hello": "^1.0.0"
  }
}

And gaia will manage the --proto_paths (includeDirs) for you, so that gRPC Protobuf Loader will know where to search and import .proto files

More about includeDirs

gaia recursively parses the protoDependencies of project foo, and its protoDependency's protoDependencies to generate the options.includeDirs option for @grpc/proto-loader

How to Write a gaia Server

Take the project hello which introduced above for example.

Since we define a Greeter service in hello.proto, we must implement the corresponding controller by ourselves.

Service controllers should be defined in directory /path/to/hello/controller which can be changed with by config controller_root.

We must provide a Greeter.js in that directory.

/path/to/hello/
  |-- controller/
  |            |-- Greeter.js

in Greeter.js, there should be an async/sync method named SayHello in exports because we defined a SayHello rpc method in service Greeter

exports.sayHello = ({name}) => ({
  message: `Hello ${name}`
})

Packages and name resolution

First the innermost package scope is searched, then the next-innermost, and so on, and at last the service name.

Assume that we have the following protocol buffer.

package foo.bar;

service Baz {
  rpc Quux (Req) returns (Res) {}
}

Then in directory controller_root, we need to create a JavaScript file foo/bar/Baz.js whose exports has a Quux method.

this object of the controller methods

There are several properties could be access by this object of the controller methods.

Reusing other controllers

We could access other controller methods by

this.controller[namespace0][namespace1]...[serviceName][methodName]

For example, we could access the Quux method by

exports.OtherMethodsOfSomeService = async function (request) {
  const data = await this.controller.foo.bar.Baz.Quux(request)
  // ...
  return something
}

Using external services

If we provide serverConfig.services for server

new Server('/path/to/service/foo', {
  ...otherConfig,
  services: {
    hello: {
      // 'hello' is a gaia server
      package: 'hello'
    }
  }
})
.listen(port)

Then, client of the service 'hello' could be accessed from the service controller of foo by:

exports.Quux = async function ({name}) {
  const {message} = await this.service.hello.SayHello({name})
  return {
    property: message
  }
}

Using plugins

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gaia.py-0.1.0.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

gaia.py-0.1.0-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

Details for the file gaia.py-0.1.0.tar.gz.

File metadata

  • Download URL: gaia.py-0.1.0.tar.gz
  • Upload date:
  • Size: 7.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.2.0.post20200210 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.8.1

File hashes

Hashes for gaia.py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ecb8586d10d2e8e07d46f02305632dae8f03f92afd2cfd8ac5fbeeee14003977
MD5 7b3b90b047b484606b61e37a83943da6
BLAKE2b-256 d384d82a5b449ac3b07ae8be2122047baa52fa99c4a0d2d4fbfd823da73211b6

See more details on using hashes here.

File details

Details for the file gaia.py-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gaia.py-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 6.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.2.0.post20200210 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.8.1

File hashes

Hashes for gaia.py-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 15fd8cfa6675bcc077c25fb03e770ed4c647be8f340a5da81b73f135cbd7c9b6
MD5 b787bc134a2e00d05ab021cf1e6d4cc8
BLAKE2b-256 f2d8b0440ed8f5a9f37faf041072a9ac9ed3ad262c5f52ce470be074200c99ad

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page