- Error codes are well defined (vs "should I return 200 OK and error message as a JSON or return 40x HTTP code?")
- no semantics ambiguity ("should I use POST for query with parameters that don't fit URL query param, or POST is only for modifying?")
- API upgrades compatibility out of the box (protobuf fields identified by numbers, not names)
Not to mention cross-platform support and autogenerating code.
I use it in multiple Flutter+Go apps, with gRPC Web transparently baked in, and it just works.
Once I had to implement chunked file upload in the app and, used to multiform upload madness, was scared even to start. But without all that legacy HTTP crap, implementing upload took like 10 mins. It was so easy and clean, I almost didn't believe that such a dreadful thing as "file upload" could be so easy. (years of fighting HTTP legacy).
Compared to the "traditional" workflow with REST/JSON the downside for me is, of course, the fact that now you can't help but care about API. With web frameworks the serialization/deserealization into app objects happens automagically, so you throw JSON objects left and right, which is nice until you realize how much CPU/Memory is being wasted for no reason.
Also, check out drop-in replacements for cases where you don't need full functionality of gRPC:
- Twirp (Twitch light version of gRPC, with optional JSON encoding, HTTP1 support and without streaming) - https://github.com/twitchtv/twirp
- Connect - "Better gRPC" https://connect.build/docs/introduction/