REST or gRPC: when to use each, and why your core speaks neither
By Dorian Chávez · founder of Hábil and integration architect ·
REST at the edge, gRPC at the core and an adapter for legacy: criteria, gRPC-Web, transcoding, tools, Spring Boot, Quarkus and Protobuf.
The question "REST or gRPC?" is usually posed as if you had to pick a side for the whole architecture. In practice, it is almost never decided once: it is decided at each boundary. An API that third parties receive, a call between two services of the same team and the connection to a twenty-year-old system call for different answers.
This is a deeper look at the "online" branch of our article on online or asynchronous integration: there we decide when it makes sense to answer instantly, and here we cover which protocol to use for it and what to do when the system at the back cannot.
This article is for the technical team that has to make those decisions: architects, development leads and the people who integrate systems at banks, insurers, fintechs or retailers. It proposes a criterion that fits in one sentence:
REST at the edge, gRPC at the core, an adapter for legacy.
It is a criterion, not a rule; at the end we look at the cases where it makes sense to break it.
What gRPC is, in five ideas
gRPC is a remote procedure call (RPC) framework. Instead of thinking in terms of resources and URLs, you think in terms of services with methods that are invoked remotely as if they were local. Five ideas define it:
- The contract comes first. The service and its messages are described in a
.protofile. By default, gRPC uses Protocol Buffers (Protobuf, a binary data-serialization format) as the interface definition language, both for the service and for the structure of the messages [1]. From the.proto, the typed client and the server skeleton are generated in whatever languages the team uses [4]. - It runs over HTTP/2 and travels in binary. gRPC is designed for HTTP/2 (the second major version of the web's transfer protocol), which offers binary framing, compression and multiplexing of several calls over a single TCP connection. Protobuf produces small messages and serializes quickly. A nuance worth keeping in mind: HTTP/2 is not exclusive to gRPC; an HTTP API with JSON can use it too [4].
- First-class streaming. Besides the simple call (one request, one response), there is server-to-client streaming, client-to-server streaming and bidirectional streaming. gRPC guarantees the order of messages within a single call [1]. It is useful for telemetry, live quotes or notifications.
- Explicit deadlines. The client states how long it is willing to wait; if that time runs out, the call ends with the
DEADLINE_EXCEEDEDerror [1]. By default gRPC sets no deadline, so a client can end up waiting practically forever [2]. When the deadline expires, gRPC cancels the call, but the application must detect that cancellation and stop the work it started [2]. The deadline can be propagated to the calls that service makes to others, so resources are not spent on an answer nobody is waiting for anymore [2][4]. - Error statuses with their own vocabulary. Every call ends with a status code from a catalog of 17 values, among them
INVALID_ARGUMENT,NOT_FOUND,PERMISSION_DENIED,UNAUTHENTICATED,RESOURCE_EXHAUSTEDandUNAVAILABLE[3]. The official codes page does not define an equivalence with HTTP codes. Whoever exposes the service to the outside must decide that translation in writing.
gRPC is not "faster REST": it is a different model, with a mandatory contract and a strict specification that, according to Microsoft, avoids the debate over URL format, verbs and response codes [4]. If you will also expose HTTP/JSON, you still have to define those routes, verbs and errors. And it is not free: the binary format cannot be read at a glance and needs the contract to be interpreted [4].
When to use REST
REST (Representational State Transfer) is a design style over HTTP oriented to resources, verbs and representations, almost always in JSON. It is the default option at the boundary: where your system meets someone you do not control.
- Third parties and integrators. Whoever consumes your API has no reason to adopt your code-generation tooling. REST/JSON reduces dependence on generated SDKs and makes manual inspection and testing easier; a partner can try a call with
curlin a minute. - Browser frontend. No browser gives the control over HTTP/2 that gRPC needs, so a gRPC service cannot be called directly from a web page [4]. Further on we look at what to do when you still want gRPC on the web.
- Human debugging. A REST request can be read, copied, pasted into a ticket and replayed. With gRPC you need tools that understand the format [4].
When to use gRPC
Consider gRPC when you control both provider and consumer, accept governing the .proto files and have a measured constraint on latency, message size or volume. The scenarios Microsoft's documentation recommends are low-latency, high-throughput microservices, real-time point-to-point communication, multi-language environments, bandwidth-constrained networks and inter-process communication on the same machine [4].
- Internal services. Between services in the same trust domain, the strong contract and the generated client prevent an entire category of errors: fields that get renamed, types interpreted differently, hand-written clients that drift out of date.
- A strong contract between teams. When several teams and languages share services, the
.protoworks as an executable agreement: if it does not compile, it does not integrate. - Volume. When there are many small calls, message size and multiplexing over one connection are noticeable. Measure with your own load before justifying the change by speed.
- Streaming. If the use case is a flow (prices, device events, the progress of a long process), gRPC offers it as part of the contract, not as an add-on.
There is one case Microsoft rules out: mass broadcast to many connected clients; gRPC has no concept of "broadcast" and each call transmits separately [4].
The browser and gRPC-Web
Since the browser cannot speak native gRPC, there is gRPC-Web: an adapted protocol, with a generated JavaScript client and a compatible proxy (the official tutorial uses Envoy) between the browser and the gRPC service; it may also require CORS configuration [5].
It has limits worth knowing before deciding. In the project's repository, server-to-client streaming is supported only in grpcwebtext mode, and client streaming and bidirectional streaming are not supported [6]. Microsoft lists it among the reasons to prefer another framework when the API must be accessible from the browser: gRPC-Web offers support, but with limitations and an additional server proxy [4].
Rule of thumb: gRPC-Web if the web application is yours and the platform already uses Protobuf; REST/JSON if third parties will consume it.
Exposing gRPC as REST without writing the logic twice
You do not have to choose between the two faces. The .proto contract can carry HTTP annotations (google.api.http) that say which route and verb correspond to each method. An intermediate component, normally the gateway or a proxy, converts the HTTP/JSON request into a gRPC call and returns the response as JSON. This mechanism is called transcoding [4][7][18]:
REST client → gateway (transcoding) → gRPC service → adapter → legacy core
gRPC client ──────────────────────→ gRPC service → adapter → legacy coreTwo examples of gateways that offer it, as a category and not as a recommendation:
- Envoy has the gRPC-JSON transcoder filter, which lets a REST client with JSON send requests over HTTP and have them forwarded to a gRPC service. It requires knowing the service's Protobuf descriptor and the HTTP annotations in the contract [7].
- Apache APISIX has the
grpc-transcodeplugin, which transforms requests and responses between HTTP and gRPC. The protos are registered through its admin API, as text or as a binary descriptor [8]. The documentation we consulted does not mention streaming; check it in your version.
The idea: one implementation, two ways to call it.
Two cautions that transcoding does not solve on its own:
- Errors. The converter translates codes, but the policy on what each one means for a third party is yours to define. Should an internal
UNAVAILABLEbe shown as a service-unavailable error, or retried first? - Security. Authentication, fine-grained authorization and quotas are concerns of the gateway and the service, not of the format.
Testing tools: what is still current
Testing a gRPC service by hand calls for a tool that understands Protobuf. This is the status we verified on October 6, 2026; any later change may alter it.
| Tool | Type | Verified status | What it is for |
|---|---|---|---|
| Postman | Graphical client | Current. Supports simple and streaming calls, JSON messages, metadata, authorization and TLS [9] | Teams that already use it for REST and want a single client |
| grpcurl | Command line | Current. Version 1.9.4 published on Aug 31, 2026 [10]. Accepts JSON, uses reflection or .proto/protoset, supports TLS and mTLS, and streaming calls [10] | Automation and diagnosis from the terminal |
| Kreya | Graphical client | Current. Version 1.21.0 dated Aug 31, 2026 in its release notes [11] [vendor] | Saved, reusable tests with a graphical interface |
| Insomnia | Graphical client | Current. Supports all four call types, .proto import and reflection [12] | A graphical alternative with schema import |
| Evans | Interactive command line | Not archived; its latest published release is from February 2023 [13]. Assess its maintenance before standardizing on it | Interactive exploration, with that caveat |
| BloomRPC | Graphical client | Do not use in new projects. Its repository is archived and read-only (last change in January 2023) [14] | Only as a historical reference |
Server reflection makes it possible to discover methods without the .proto, but it must be enabled on the service and, in production, you should decide who gets to see it.
Java: Spring Boot and Quarkus
Java is a useful case because both frameworks have a documented path to gRPC. The versions that follow are the ones verified on October 6, 2026.
Spring Boot. Spring gRPC is an official Spring project, with version 1.0.0 published on Dec 4, 2025. As of the cutoff, the latest stable version on GitHub is 1.1.1 (Aug 21, 2026) [15]. Version 1.1.0 (Jun 10, 2026) migrated the auto-configuration to Spring Boot 4.1.0, and the 1.0.x branches target Boot 4.0 [15]. For a new codebase on Spring Boot 4.1 there is an official path; with earlier versions, check compatibility with the 1.0.x line.
Quarkus. gRPC is added with the quarkus-grpc extension: it implements and consumes services, generates code from the .proto, integrates with the reactive model, and supports TLS and mutual TLS, xDS and in-process communication [16]. Quarkus recommends the Vert.x-based gRPC server because it is more flexible and better integrated into the ecosystem, and that server can serve HTTP and gRPC on the same port if quarkus.grpc.server.use-separate-server=false is configured [16]. On Maven Central, the latest 3.x version of the extension as of the cutoff is 3.40.1 (Sep 30, 2026); there is also a 4.0.0.Beta1 from that same month, which is a pre-release [16].
Both work: choose the framework the team already masters.
Whatever the framework, the same practices matter: a single contracts repository, interceptors for identity and traces, a common error policy, health checks and mandatory deadlines on every client.
Evolving Protobuf contracts: how not to break anyone
The contract is the product. Changing it carelessly breaks the clients that did not find out. The official Protobuf guide sets out clear rules [17]:
- Adding fields is safe. Old code ignores new fields when reading, and new code uses default values with old messages.
- Adding values to an
enumis safe in the binary format; even so, check that clients and business rules account for the new value (in languages with closed enums, such as Java, an unknown value arrives as a separate case). - A field's number is never changed or reused once the message is in use, because that number identifies the field on the wire.
- When you remove a field, you reserve its number (and, if you use JSON, its name too) so nobody reuses it by accident.
- Moving fields into or out of an existing
oneofis risky: information can be lost when serializing and reading.
Google's API design guide applies to both REST and RPC and points to its proposals AIP-180 (compatibility) and AIP-185 (versions) [18]. The operating rule is short:
- Publish the
.protofiles as a versioned product. - Validate compatibility in continuous integration (with a breaking-change detection tool).
- Keep contract tests between the service and its consumers.
- Treat any non-additive change as a new version of the API, with a coexistence period.
Why your core speaks neither
Here is the point almost no protocol comparison touches. The core systems of an insurer, a bank or a retailer with decades of history tend to speak neither REST nor gRPC, and their limitations explain why:
- They expose no APIs that can be called, and they do not notify you when something changes.
- They only accept data in an exchange zone, tables or files, and process it at their own pace, often in batches, triggering a process of their own.
- They can take very little load: a burst of online calls overwhelms them.
- The response arrives when the process finishes, not when the request was sent, and sometimes it can only be found by reading another table or file again.
Choosing REST or gRPC for the layer above does not change that. What you need in between is an adapter: a component that speaks the core's language inward and the new contract outward. And when the model, errors, security or transactions on the two sides do not match, the adapter should be an anti-corruption layer, the pattern Eric Evans described in Domain-Driven Design [19].
It is a facade between subsystems that do not share semantics, so that external dependencies do not constrain the application's design; it can be an internal component or an independent service [19].
The difference from a proxy is important:
| Transcoding at the gateway | Adapter or anti-corruption layer | |
|---|---|---|
| What it translates | Protocol and format: HTTP/JSON ↔ gRPC/Protobuf | Business semantics: model, errors, transactions and access rules |
| Who knows it | The gateway, with the contract's descriptor | Whoever knows the core and its history |
| Example | Convert POST /policies into the CreatePolicy call | Convert CreatePolicy into writes to exchange tables, triggering a procedure and waiting for the result |
| What it prevents | Duplicating the service's implementation | The core's historical decisions leaking into the public contract |
Four tasks tend to fall to the adapter, and it is worth writing them down before they get diluted in the code (a design criterion; the pattern itself is described in [19]):
- Translate the data objects and validate invariants at that boundary, including input sanitization.
- Normalize errors: a proprietary return code or a row in an errors table becomes a gRPC status and, from there, if appropriate, an HTTP code, with the explicit policy discussed above.
- Protect the core. A core has a pace it can sustain. If a mass issuance sends thousands of movements and the core processes a few dozen per minute, the online call is not the way to deliver them: you need a queue or a load-leveling mechanism [20], unless the caller requires a low-latency synchronous response. A queue requires defining idempotency, retries, handling of failed messages, ordering where it applies and status lookup by correlation identifier [20]. That is the subject of the next article in this series.
- Observe: a correlation identifier on every call, to reconstruct what happened between the new contract and the core, and structured logs to diagnose translation failures.
Costs: more latency, one more service to operate and deciding whether the layer is permanent [19]. Microsoft recommends limiting it to translating, without concentrating business rules or orchestration in it [19].
Decision table
| Situation | Reasonable leaning | Why | When it would change |
|---|---|---|---|
| Public API or one for external integrators | REST/JSON with OpenAPI | Common ground, easy to test and debug | If the consumer accepts generated SDKs and gains from streaming or efficiency |
| Your own web application | REST/JSON, or gRPC-Web if the platform is already Protobuf | No extra proxy in the first case | When you prefer a single contract across the stack and accept gRPC-Web's limitations |
| Internal services of the same team or domain | gRPC | Strong contract, generated client, explicit deadlines and statuses | If the team does not operate Protobuf well or the volume does not justify the change |
| Many small or low-latency calls | gRPC (measured with your load) | Compact message and multiplexing | If measurement shows no useful difference |
| Continuous flows (prices, events, progress) | gRPC with streaming, or a messaging system | Streaming within the contract | If you need broadcast to many clients, evaluate another tool |
| Same logic, two types of consumer | gRPC with transcoding | One implementation, two faces | If the two contracts diverge a lot, maintain two facades |
| Legacy core (no API, with an exchange zone in tables or files, batch processing) | Adapter / anti-corruption layer inward | The core speaks neither | If the core already exposes a stable API, adapt less |
| Mass load into a core with a limited pace | Queue or asynchronous process, not an online call | Protects the core | If the core absorbs the volume without degrading |
When to break the guiding phrase
"REST at the edge, gRPC at the core, an adapter for legacy" works as a starting point. There are reasonable cases where it makes sense to depart from it:
- All REST. With a small team, moderate volume and no streaming, Protobuf can cost more than it gives.
- gRPC all the way to the edge. If external consumers are few and accept generated SDKs, you avoid a layer.
- No adapter. If the core already offers a modern, stable API, the adapter shrinks to a thin mapping.
- Asynchronous instead of synchronous. If the problem is the core's pace and not the protocol, what helps is a queue, an event or a callback with a correlation identifier.
What does make sense to keep: decide per boundary, measure before justifying, and write the contract before writing the code.
Closing: what to take to your next meeting
Before choosing a protocol, three questions organize the discussion: who consumes the interface and who controls its clients, what language the system at the back speaks and what pace it can sustain. If the system at the back speaks neither or is slower than whoever calls it, and the measured load demands decoupling intake from processing, it makes sense to prioritize the adapter and consider a queue before changing protocols.
What Hábil does
Hábil works in the space between existing systems and new layers: we aim for a core that speaks neither REST nor gRPC to be able to serve your channels and partners without slowing operations. To join your APIs and channels, see the page Unify your operations and your channels; to update the core at its own pace, Modernize the core without slowing the business. The first conversation, at no cost, starts from the map of your integrations and the points where a contract or a core with a limited pace could cost you money, time or evidence. Write to us on WhatsApp.
Accessed on Oct 6, 2026. The versions and dates of tools and projects change; those marked [vendor] are figures or statuses published by the vendor itself.
References
- gRPC, Core concepts, architecture and lifecycle (Protobuf as IDL; four call types; ordering within a call;
DEADLINE_EXCEEDED). https://grpc.io/docs/what-is-grpc/core-concepts/ - gRPC, Deadlines (no default deadline; server-side cancellation; propagation). https://grpc.io/docs/guides/deadlines/
- gRPC, Status codes and their use in gRPC (catalog of 17 codes). https://grpc.io/docs/guides/status-codes/
- Microsoft Learn, Compare gRPC services with HTTP APIs (comparison table; HTTP/2 is not exclusive to gRPC; recommended scenarios; browser limits; gRPC-Web and transcoding; mass broadcast). https://learn.microsoft.com/en-us/aspnet/core/grpc/comparison?view=aspnetcore-10.0
- gRPC, gRPC-Web Basics (generated client, Envoy, CORS). https://grpc.io/docs/platforms/web/basics/
- grpc-web, repository README (server streaming only in
grpcwebtextmode; client and bidirectional streaming, not supported). https://github.com/grpc/grpc-web - Envoy, gRPC-JSON transcoder. https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_json_transcoder_filter
- Apache APISIX,
grpc-transcodeplugin. https://apisix.apache.org/docs/apisix/plugins/grpc-transcode/ - Postman, gRPC request interface. https://learning.postman.com/docs/use/send-requests/protocols/grpc/grpc-request-interface/
- FullStory, grpcurl (version 1.9.4 published Aug 31, 2026 according to the GitHub API) [vendor]. https://github.com/fullstorydev/grpcurl/releases/tag/v1.9.4
- Kreya, Release notes (1.21.0, Aug 31, 2026) [vendor]. https://kreya.app/docs/release-notes/
- Kong, Insomnia: gRPC requests. https://developer.konghq.com/insomnia/grpc-requests/
- ktr0731, Evans (repository; latest published release in Feb 2023, repository not archived). https://github.com/ktr0731/evans
- BloomRPC (archived repository, read-only; last change in Jan 2023). https://github.com/bloomrpc/bloomrpc
- Spring, Spring gRPC Reference and Releases (official project; 1.0.0 of Dec 4, 2025; 1.1.0 of Jun 10, 2026 migrates the auto-configuration to Spring Boot 4.1.0; 1.1.1 of Aug 21, 2026) [vendor]. https://docs.spring.io/spring-grpc/reference/ · https://github.com/spring-projects/spring-grpc/releases
- Quarkus, gRPC and gRPC reference guide (recommended Vert.x server; shared port with
quarkus.grpc.server.use-separate-server=false); versions ofio.quarkus:quarkus-grpcaccording to Maven Central metadata (3.40.1 and 4.0.0.Beta1) [vendor]. https://quarkus.io/guides/grpc/ · https://quarkus.io/guides/grpc-reference/ · https://repo1.maven.org/maven2/io/quarkus/quarkus-grpc/maven-metadata.xml - Protocol Buffers, Language Guide (proto3) (fields, numbers, reservations, enums,
oneof). https://protobuf.dev/programming-guides/proto3/ - Google Cloud, API Design Guide (REST and RPC; compatibility AIP-180; versions AIP-185; HTTP mapping for transcoding). https://docs.cloud.google.com/apis/design
- Microsoft Learn, Anti-Corruption Layer pattern (Azure Architecture Center; pattern described by Eric Evans in Domain-Driven Design). https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer
- Microsoft Learn, Queue-Based Load Leveling pattern (queue between the caller and the service; not suitable when a low-latency synchronous response is required; at-least-once delivery semantics, idempotency, dead-letter queue, ordering). https://learn.microsoft.com/en-us/azure/architecture/patterns/queue-based-load-leveling
- Modernize the core without slowing the business →
- Unify your operations and your channels →
- APIs ready for AI agents: what your system needs so an agent can use it with control →
Does your operation face these challenges?
Prefer email? Write to us at hola@habil.mx