Architecture15 min

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:

  1. The contract comes first. The service and its messages are described in a .proto file. 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].
  2. 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].
  3. 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.
  4. Explicit deadlines. The client states how long it is willing to wait; if that time runs out, the call ends with the DEADLINE_EXCEEDED error [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].
  5. 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_EXHAUSTED and UNAVAILABLE [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 curl in 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 .proto works 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 core

Two 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-transcode plugin, 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 UNAVAILABLE be 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.

Testing tools: what is still current
ToolTypeVerified statusWhat it is for
PostmanGraphical clientCurrent. Supports simple and streaming calls, JSON messages, metadata, authorization and TLS [9]Teams that already use it for REST and want a single client
grpcurlCommand lineCurrent. 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
KreyaGraphical clientCurrent. Version 1.21.0 dated Aug 31, 2026 in its release notes [11] [vendor]Saved, reusable tests with a graphical interface
InsomniaGraphical clientCurrent. Supports all four call types, .proto import and reflection [12]A graphical alternative with schema import
EvansInteractive command lineNot archived; its latest published release is from February 2023 [13]. Assess its maintenance before standardizing on itInteractive exploration, with that caveat
BloomRPCGraphical clientDo 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 enum is 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 oneof is 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:

  1. Publish the .proto files as a versioned product.
  2. Validate compatibility in continuous integration (with a breaking-change detection tool).
  3. Keep contract tests between the service and its consumers.
  4. 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:

Why your core speaks neither
Transcoding at the gatewayAdapter or anti-corruption layer
What it translatesProtocol and format: HTTP/JSON ↔ gRPC/ProtobufBusiness semantics: model, errors, transactions and access rules
Who knows itThe gateway, with the contract's descriptorWhoever knows the core and its history
ExampleConvert POST /policies into the CreatePolicy callConvert CreatePolicy into writes to exchange tables, triggering a procedure and waiting for the result
What it preventsDuplicating the service's implementationThe 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

Decision table
SituationReasonable leaningWhyWhen it would change
Public API or one for external integratorsREST/JSON with OpenAPICommon ground, easy to test and debugIf the consumer accepts generated SDKs and gains from streaming or efficiency
Your own web applicationREST/JSON, or gRPC-Web if the platform is already ProtobufNo extra proxy in the first caseWhen you prefer a single contract across the stack and accept gRPC-Web's limitations
Internal services of the same team or domaingRPCStrong contract, generated client, explicit deadlines and statusesIf the team does not operate Protobuf well or the volume does not justify the change
Many small or low-latency callsgRPC (measured with your load)Compact message and multiplexingIf measurement shows no useful difference
Continuous flows (prices, events, progress)gRPC with streaming, or a messaging systemStreaming within the contractIf you need broadcast to many clients, evaluate another tool
Same logic, two types of consumergRPC with transcodingOne implementation, two facesIf 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 inwardThe core speaks neitherIf the core already exposes a stable API, adapt less
Mass load into a core with a limited paceQueue or asynchronous process, not an online callProtects the coreIf 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

  1. 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/
  2. gRPC, Deadlines (no default deadline; server-side cancellation; propagation). https://grpc.io/docs/guides/deadlines/
  3. gRPC, Status codes and their use in gRPC (catalog of 17 codes). https://grpc.io/docs/guides/status-codes/
  4. 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
  5. gRPC, gRPC-Web Basics (generated client, Envoy, CORS). https://grpc.io/docs/platforms/web/basics/
  6. grpc-web, repository README (server streaming only in grpcwebtext mode; client and bidirectional streaming, not supported). https://github.com/grpc/grpc-web
  7. Envoy, gRPC-JSON transcoder. https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_json_transcoder_filter
  8. Apache APISIX, grpc-transcode plugin. https://apisix.apache.org/docs/apisix/plugins/grpc-transcode/
  9. Postman, gRPC request interface. https://learning.postman.com/docs/use/send-requests/protocols/grpc/grpc-request-interface/
  10. 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
  11. Kreya, Release notes (1.21.0, Aug 31, 2026) [vendor]. https://kreya.app/docs/release-notes/
  12. Kong, Insomnia: gRPC requests. https://developer.konghq.com/insomnia/grpc-requests/
  13. ktr0731, Evans (repository; latest published release in Feb 2023, repository not archived). https://github.com/ktr0731/evans
  14. BloomRPC (archived repository, read-only; last change in Jan 2023). https://github.com/bloomrpc/bloomrpc
  15. 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
  16. Quarkus, gRPC and gRPC reference guide (recommended Vert.x server; shared port with quarkus.grpc.server.use-separate-server=false); versions of io.quarkus:quarkus-grpc according 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
  17. Protocol Buffers, Language Guide (proto3) (fields, numbers, reservations, enums, oneof). https://protobuf.dev/programming-guides/proto3/
  18. 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
  19. 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
  20. 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