These three get compared as though one must be best, and they are better understood as answers to different questions about who consumes the API and how much control you have over them. Choosing by technical merit alone is how teams end up with a GraphQL endpoint serving one internal client, or gRPC on a public API nobody can call from a browser.
REST over JSON remains the right default for anything public or partner-facing. Every developer knows it, every tool supports it, it works from a browser and a curl command, and it caches over HTTP without any additional machinery. Its weakness is well known - fixed response shapes mean clients over-fetch, and complex screens need several round trips - and for most APIs that weakness is smaller than the interoperability it buys.
GraphQL earns its place when many different clients need different subsets of the same data, and particularly when mobile clients are round-trip sensitive. One request, exactly the fields needed, no negotiation with the API team every time a screen changes. The costs are real and often understated: caching is harder because everything is one POST to one endpoint, arbitrary query depth is a denial-of-service surface unless you limit it, and the naive resolver implementation produces the N+1 query problem by construction, which is why dataloader batching is effectively mandatory rather than an optimisation.
gRPC is for service-to-service traffic inside your own estate. Binary encoding and HTTP/2 multiplexing make it fast, and the schema is enforced by generated code in both directions, which catches contract mistakes at compile time rather than in production. It is a poor fit for public APIs - browsers cannot speak it without a proxy, debugging is harder without text on the wire, and any partner integrating with you now needs tooling they may not have.
The version of this decision that actually matters is not the protocol but the contract. Whatever you choose, the interface should be defined explicitly - OpenAPI, a GraphQL schema, protobuf definitions - and that definition should be the source of truth from which clients and validation derive. Teams that write the implementation first and document it afterwards end up with a contract that describes what the code happens to do, including its accidents.
In practice most estates end up with two: REST for the public and partner surface, because interoperability wins there, and gRPC internally between services where you control both ends and want the performance and the type safety. GraphQL is worth adding when you have a specific client-diversity problem it solves. Adopting it because the API feels inflexible, without that problem, tends to move the complexity into resolvers rather than remove it.