4

protoc-gen-grpc-gateway-ts - Clean, Idiomatic TypeScript for grpc-gateway

 3 years ago
source link: https://cashapp.github.io/2021-03-26/protoc-gen-grpc-gateway-ts-clean-idiomatic-typescript-for-grpc-gateway
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

protoc-gen-grpc-gateway-ts - Clean, Idiomatic TypeScript for grpc-gateway

protoc-gen-grpc-gateway-ts is a TypeScript client generator for the grpc-gateway project. It generates idiomatic TypeScript clients that connect the web frontend and golang gRPC backend fronted by grpc-gateway.

Background

In Cash App, we prefer gRPC as the way of communication. We also use grpc-gateway to expose our gRPC services to legacy and web clients over HTTP/1.

This worked well until the day we needed to start integrating grpc-gateway with Web clients. To build a Web client for a gRPC enabled service, there are two options.

  1. Use protoc-gen-openapiv2 (previously protoc-gen-swagger) to generate an OpenAPI schema then run an OpenAPI generator to get the Web client

  2. Use grpc-web with envoy grpc-web filter to pipe the request through.

Although both are valid options and have reasonable numbers of users, after some investigation both options have their own pain points.

Pain points

protoc-gen-openapiv2

OpenAPI has both popularity and maturity. However, going down this path means

  • proto -> OpenAPI -> Typescript

  • Generated clients only support unary gRPC calls, where grpc-gateway can do server side streaming.

grpc-web

grpc-web is a Javascript implementation of gPRC for browsers. To use it, we found:

  • grpc-web client requires either a proxy, or envoy with a filter in between to do the translation.
    • The proxy is an extra layer for our service while we have grpc-gateway already
    • For the envoy + filter option it was not easy to set up.
  • The generated grpc-web client is verbose and hard to use.
    • Setter is required for every desired field and there is no way to simplify it.
  • TypeScript support
    • It is experimental
    • 3 assets files(1 .js & 2 .ts files) generated for a single proto file. Here is an example of the grpc-web proto generation output.

Solution

To solve the pain points found in both protoc-gen-openapiv2 & grpc-web, we build protoc-gen-grpc-gateway-ts. It comes with the following features:

  1. Idiomatic TypeScript clients and messages
  2. Single step - proto -> TypeScript
  3. Supports both unary calls and server side streaming
  4. POJO message construction guarded by message type definitions.
  5. Standard protoc plugin that can be run alongside any other plugins

After the creation of protoc-gen-grpc-gateway-ts, we have

  • Battle tested against 1000s of proto files in Cash App & Square for proto generation.
  • Used the generated code extensively for our internal web admin modules, giving the following benefits:
    • POJO message construction greatly simplifies the process to make a request, a simple request can be one liner.
    • TypeScript is widely supported in main-stream IDEs, code completion, documentation flows naturally to help out daily development
    • The ability to do server side streaming means the Web client is feature parity with all other clients.
  • Is gaining more and more traction across internal teams

Here is an example of a simple counter service

The proto file:

// file: counter.proto
message Request {
  int32 counter = 1;
}

message Response {
  int32 result = 1;
}

service CounterService {
  rpc Increment(Request) returns (Response);
}

Generated TypeScript file:

// file: counter.pb.ts
import * as fm from "./fetch.pb"

export type Request = {
  counter?: number
}

export type Response = {
  result?: number
}

export class CounterService {
  static Increment(req: UnaryRequest, initReq?: fm.InitReq): Promise<UnaryResponse> {
    return fm.fetchReq<UnaryRequest, UnaryResponse>(`/main.CounterService/Increment`, {...initReq, method: "POST", body: JSON.stringify(req)})
  }
}

Example usage of the generated counter.pb.ts

import {CounterService} from './counter.pb'

// increase the given number once  
async function increase(base: number): Promise<number> {
  const resp = await CounterService.Increase({counter: base})
  return resp.result
} 

Open Sourcing

It was originally planned to be open-sourced via Square/Cash App’s public Github account. However, with the help from Google Engineers, it now lives in an ideal location: grpc-ecosystem, which is next to grpc-gateway.

End Note

It’s a fun and resourceful journey to create protoc-gen-grpc-gateway-ts. Hope it soon will start getting traction and contributions around the globe.

Posted by Yun Lai on March 26, 2021

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK