Hacker News new | ask | show | jobs
by zelcon 642 days ago
Please, people, just use GRPC or Thrift. This stuff makes me want to vomit.
4 comments

GRPC and Thrift can't express ADTs (enums with data) easily, but OpenAPI can. That's worth a lot in my book.

Another advantage of OpenAPI is that you can write your specifications using Rust types (as we do at Oxide with Dropshot: https://docs.rs/dropshot)

edit: Apparently protobuf 3 does have oneof: https://protobuf.dev/programming-guides/proto3/#oneof. They look like they solve the problem but I can't vouch for it, and they appear to have some edge cases ("only the last member seen is used in the parsed message"). Thrift doesn't appear to, still.

And I do think being able to write the spec using Rust types is really nice -- you still get an OpenAPI document as an artifact, and (for external users) you get wide client compatibility.

If you're building maintainable servers you should write the doc first and the codegen from there. Otherwise you're gonna be in a world of hurt when some junior changes your datatype or if you need to version and maintained both versions simultaneously from the same or similar endpoints.
You're right that this is a very difficult problem, but writing the document first doesn't give you much over generating it from types.

We actually have a plan for supporting multiple versions, and conversions between the corresponding types, using Dropshot as the place where that's coordinated.

> writing the document first doesn't give you much over generating it from types.

There's a required top-level parameter called version. As you are writing the document, it's in your face.

With Dropshot, the version is passed in as a function argument to the OpenAPI generator.
You will find out the problems with your approach.
This "stuff" allows for easy exchange of API definitions and Arazzo goes to the next level to define the semantics of the process of combining API calls.

gRPC requires brittle compilation of the protobuf definitions that has impacted every marshalling/serialization protocol for remote procedure calls since XDR.

Whether you like it or not, HTTP/JSON are the lingua franca of the internet (at least the API side of things). Protobuf is good if you are in control of both sides of the API, less so if you are just the server. It also is much less self-documenting than JSON Schema/OpenAPI.

Apples and oranges? gRPC might sometimes fit the bill for server-to-server use cases, but it's completely unsuitable for integration with SPAs; grpc-web was never well-supported, and is now dead. I don't understand the "want to vomit" perspective. That implies that adding protoc to your build toolchain and using an opaque binary format/protocol is somehow much more palatable than well-documented JSON / REST over HTTP. What am I missing?
ConnectRPC works over the web and I've built several web apps (SPAs) with it. It works with JSON by just setting the Content-Type header to `application/json`. You can add compression easily on the backend with a single option. Generating typed stub code is one line (`buf generate`). Connect-ES is awesome - it's a Typescript-first protobuf library for the web.

Interacting with the API is very simple, for example:

        const resp = await gameEventClient.getRecentAnnotatedGames({
          limit: annotatedPageSize,
          offset: recentAnnotatedGamesOffset,
        }); 
        setRecentAnnotatedGames(resp.games); 

These function calls are typed and a lot of the code is auto-generated from the .proto spec (i.e. above the gameEventClient and getRecentAnnotatedGames are auto-generated code). On the server side the code is also obviously auto-generated. It all works seamlessly. Even the documentation for the API can be auto-generated. See for example:

https://buf.build/domino14/liwords

The above documentation was auto-generated from my protobuf files. This project uses ConnectRPC on the backend and on the front-end SPA. To me it seems so much simpler and better than the way I've seen people use OpenAPI - where many people seem to create the code _before_ creating the spec. I actually haven't found a good Go generator of stub code from an OpenAPI spec. With ConnectRPC it just works, it's simple, easy, fast, etc. It's easy to add interceptors to do things like parse the http request for an Auth or Cookie header and then insert the user ID etc back into the context for the different service functions to handle whatever needs to be done with the authenticated user.

I could ask the same question - what am I missing?

Thanks for the thoughtful reply. Glad you found something you like that's solving your problems! The docs you linked do look nice. Ironically, I noticed this quote there:

> "It's much easier to use the API with JSON + a web browser, but the protobuf option is still available..."

... which sort of underscores my perspective (which is admittedly strongly biased towards / empathetic with the API consumer side).

Yep, but the awesomeness of ConnectRPC is that you can use the JSON api in the web browser, and indeed I do that in this project. We don't have to, and as a matter of fact it's a little less performant, but it's easier to debug and hack.
Cool beans. Thanks for the response -- TIL!

[meta: IMHO this is an exemplary HN interaction; instead of shouting past each other, we shared very different points of view, and one of us learned something potentially useful.]

Any idea how monstrous migrating from gRPC to ConnectRPC might be, on the backend?

I've never used gRPC before, but from what I can tell, ConnectRPC tries to be compatible with it.

https://connectrpc.com/docs/go/grpc-compatibility/

It might not be super simple. Good interaction, I agree :)

Connect libraries are 100% compatible with gRPC - in many cases, they implement the gRPC spec more faithfully than the core libraries themselves: https://buf.build/blog/grpc-conformance-deep-dive

TLDR: there should be no migration cost.

ConnectRPC here but yeah same idea.