Igor's corner

REST vs gRPC for a public API in 2026

Published on

The problem

I am adding a server to my Flutter app Doshboard (intentionally overengineered for learning - don’t do this in your pet projects). Internally the microservices already talk gRPC, but the public API conventionally uses REST, so I put a REST - gRPC transcoder in front of them.

Do I need that transcoder though? My users wouldn’t mind lower data usage and better streaming, and I wouldn’t mind deleting a translation layer - especially that Dart is officially supported. So the question for this post: can I drop the transcoder and run gRPC end-to-end in 2026, or are the old problems still there?

Goals

What do I want from the client-server API:

  • Cheap on the wire - less data and CPU, which on mobile means cellular data and battery.
  • A strict shared contract - one source of truth both ends are generated from, and tests are ran against.
  • Native bidirectional streaming - real-time sync without bolting on a second protocol.
  • Works on every client - mobile, desktop, and web.
  • Minimal extra infra - no transcoder, no separate streaming server/load balancer.

The options

The current set of available options seems to be:

  • Translate to REST - gRPC-gateway or Envoy generate a REST API from the .proto specification. The conventional default. Both actively maintained.
  • gRPC + gRPC-Web - gRPC on the wire: native HTTP/2 + protobuf for mobile/desktop, gRPC-Web (re-encoded, via a proxy such as Envoy) for browsers. Core gRPC is active; gRPC-Web is in maintenance mode.
  • Connect - mostly gRPC’s efficiency (binary protobuf over HTTP/2) but reshaped to talk HTTP with some quality a better observability. Native clients reach gRPC directly; web still needs a Connect - gRPC bridge (Envoy or a Connect edge), transcoding is lighter than from JSON though. Actively maintained.

Why even consider gRPC

gRPC being the more capable protocol isn’t really controversial:

  • Performance: less data on the wire, fewer CPU cycles serializing, one reused connection - less latency and battery drain.
  • Contract-first: the .proto always comes first by design. REST’s OpenAPI codegen isn’t that great, so you end up implementing API first and documenting it later which is never done. With gRPC spec tests and server mocks are free.
  • Streaming: native, bidirectional, same stack and types. REST streams one way at best (chunked/SSE); bidirectional needs a second protocol (WebSocket), often its own server and load balancer.
  • Flow control: cancellation, deadlines, backpressure built in.

So is it realistic to gain all these benefits in a multiplatform app in 2026?

Option 1: Translate to REST

The conventional default, and what my transcoder does today. It’s nearly free if Envoy is already your edge - the gRPC-JSON transcoder is just a filter. gRPC-gateway does the same as a standalone proxy behind any ingress, at the cost of one more service to run. The control group we compare to.

Both gRPC-gateway and Envoy are actively maintained.

Option 2: gRPC + gRPC-Web

Here you can get all the benefits, but it very much depends on the client platform.

Native (mobile, desktop)

Full gRPC gets you all the benefits mentioned above. If you don’t have web clients - the choice is clear, or is it?

Their client lives in uncontrolled environment, meaning things like CGNAT, TLS inspection proxies, and corporate DPI firewalls.

CGNAT seems not to be an issue, except for dropping a connection from time to time, especially if it sits idle, but first of all this is not an unrecoverable error, and secondly, and most importantly, this issue affects any kind of streaming, including the ones REST API would rely on, namely web-sockets and SSEs.

Majority of firewalls and proxies also shouldn’t be an issue, since traffic is HTTP/2 encrypted with TLS - it doesn’t look too different from a web socket connection. What is an issue is TLS terminating proxies, if they downgrade the ‘internal’ leg of their connection to HTTP/1.1. Though I am yet to encounter one in the wild, and don’t expect my users to be the ones who would.

Web

Browsers can’t speak native gRPC simply because they don’t expose the API specific to any given HTTP version, instead they want web developers not to care what generation of HTTP is used, so it is abstracted out. This is great for the REST, but gRPC needs control over individual HTTP/2 frames, which makes it not a viable option for browser usage as is.

Couple of years ago Google was pushing gRPC-web as a way of covering the gap. The idea behind it was that gRPC is carried, with minimal changes, over a plain HTTP request, with a proxy such as Envoy translating it back to gRPC. You can see this even in Dart’s gRPC package structure: the grpc package doesn’t list web as a supported platform, yet ships a separate grpc_web.dart library just for the browser. That is why.

It has a bunch of limitations, such as limited streaming capabilities, no flow control, limited cancellation, CORS preflight penalties, but what is more important is that it is now in maintenance mode. Because it was built on two of Google’s own libraries - that have since been archived, maintaining the protocol became too cumbersome. The current official recommendation is to use gRPC-Gateway, which brings us back to option 1.

Other downsides

  • Debugging & tooling: while tools like Postman or Bruno do support gRPC, and there is grpcurl to replace curl for the CLI interactions - you still lose ability to glance at traffic in your dev tools or request logs, which does reduce the debuggability of the set up.
  • Caching & CDNs: since every call is a POST on a long-lived connection - cache goes out of the window basically, so for a read heavy application - you may even lose in terms of speed.
  • Load balancing: a non-issue in practice - a public REST API already sits behind an L7 reverse proxy, the only extra requirement is that it be gRPC-aware, which all the modern ones are.
  • HTTP semantics: your monitoring tool should be aware of, for example, gRPC error codes. You can’t anymore rely on error detection based on HTTP status codes.

Core gRPC is actively maintained; gRPC-Web is in maintenance mode.

Option 3: Connect

Connect is an attempt to fix most of the issues gRPC presents while keeping the efficiency. It’s a new protocol that is designed to be fully compatible with generic HTTP, which resolves a lot of the downsides gRPC’s HTTP/2 reliance brings.

Pros:

  • Stub generation from .proto specification
  • Binary protobuf payloads
  • Works over any version of HTTP, including 1.1, which makes it:
    • Browser native
    • curl-able
    • Cacheable for calls with no side effects, since they can use GET
    • Error detectable, since they now live in the HTTP response
    • Removes the need of interop layer on the client
  • Important for my case it has official dart package

Cons:

  • You still need a translation layer, though less intensive one compared to REST since it is just repackaging of a packet rather than full remarshalling
  • The protocol is much less adopted, so less tooling, less support
  • Streaming is still limited on web, mostly individual requests are used

Actively maintained by CNCF.

Summary

Capability Translate to REST gRPC + gRPC-Web Connect
Web support βœ… βœ… via proxy βœ…
Low bandwidth (client) ❌ JSON βœ… βœ…
Bidir streaming ❌ βœ… native / ❌ web βœ… native / ❌ web
Caching / CDN βœ… ❌ βœ…
Tooling / debugging βœ… ⚠️ binary ⚠️ binary / βœ… JSON
Monitoring on HTTP status βœ… ❌ βœ…
Network robustness βœ… ⚠️ native needs e2e HTTP/2 βœ…
Strict client contract ⚠️ OpenAPI βœ… proto βœ… proto
Dart support βœ… βœ… βœ…
Edge translation heavy transcoder proxy (web) light bridge (web)
Maintenance βœ… ⚠️ gRPC-Web in maintenance βœ…
Ecosystem / adoption βœ… huge βœ… mature ⚠️ young

The conclusion

So is gRPC viable in 2026?

I’d say no. Large projects, the ones that actually may benefit from such a decision, are very dependent on caching, on having clear visibility into the data, and on supporting as many clients as possible - which basically rules out gRPC for them, in both web and non-web forms. The fact that gRPC-Web is in maintenance mode makes this ruling even simpler.

Connect, on the other hand, is tempting. I came into writing this post thinking I’d choose it for my project, for research purposes if nothing else, but looking at the picture we have here - it’s still a bunch of complexity for a fairly modest gain. So I’d say it still shouldn’t be a choice, unless both performance and robustness are of the highest priority.

To conclude: gRPC to REST conversion still should be the default. If you need to keep the amount of data between client and server to a minimum, or you need the most performant streaming you can get, and reliability is not an issue - gRPC is the choice. If you need it to be reliable too, though - go with Connect.

I will continue with the REST API for my project.