gRPC vs REST vs GraphQL vs Connect: API Protocol Comparison 2026

gRPC vs REST vs GraphQL vs Connect: API Protocol Comparison 2026

gRPC vs REST vs GraphQL vs Connect: API Protocol Comparison 2026

Last Updated: October 2026

Most arguments about gRPC vs REST vs GraphQL vs Connect are really arguments about something else. Teams say they are choosing a wire format, but what they are actually choosing is who owns the contract, where errors are represented, what an intermediary such as a CDN or load balancer is allowed to understand, and how much machinery a browser needs before it can call your service. Picking on benchmark headlines alone is how organizations end up with a binary protocol that their corporate proxy mangles, or a flexible query language that turns every dashboard refresh into an unbounded fan-out.

The landscape also moved. GraphQL shipped its first specification edition in almost four years, OpenAPI 3.2 formalized streaming and the QUERY verb, and the IETF published the HTTP QUERY method as RFC 10008 in June 2026. Connect RPC sits in the CNCF Sandbox, and gRPC over HTTP/3 is still a proposal rather than a default.

This guide compares the four at the level where the differences are real: framing, headers and trailers, error models, caching, streaming, schema evolution and operations. Where I give numbers they are derived from the protocols themselves or are explicitly labelled illustrative, because published cross-protocol benchmarks are rarely comparable.

What this covers: the wire mechanics of each protocol, a decision framework with a matrix, the failure modes that bite in production (proxies, resolver fan-out, trailers, caching), browser, mobile and IoT considerations, and a practical recommendation for mixing protocols instead of picking one.

What Changed for October 2026

This is a full rewrite of the April 2026 article. Several claims in that version did not survive verification and have been removed or corrected.

  • Removed the benchmark table. The earlier post presented latency, throughput and payload figures as “representative numbers from 2026 benchmarks” without a source or a method. They are gone. The mechanism-level analysis below explains where differences come from instead.
  • Corrected the gRPC status code count. The earlier matrix said gRPC has 13 standard error codes. The official gRPC status code documentation lists 17, numbered 0 (OK) through 16 (UNAUTHENTICATED).
  • Corrected the Connect description. Connect is not “gRPC’s wire format over HTTP/1.1.” It is a family of libraries that speaks three protocols: its own Connect protocol, gRPC and gRPC-Web. The Connect protocol is a distinct, simpler HTTP protocol that is not wire-identical to gRPC. Also, bidirectional streaming still requires HTTP/2, a point the earlier post blurred with a “headers hack.”
  • Corrected the browser and gRPC statements. Browsers cannot speak native gRPC because JavaScript fetch cannot control HTTP/2 framing or read trailers. That is a framing and API limitation, not a slow-HTTP/2 problem.
  • Removed unsourced adoption claims. Statements such as “60% of Google’s internal RPC traffic” and “39% of microservice architectures” had no traceable source and are dropped.
  • New standards context. GraphQL September 2025 edition, OpenAPI 3.2, RFC 10008 (HTTP QUERY), the status of GraphQL over HTTP, the GraphQL Composite Schemas work, and the state of gRPC over HTTP/3.

Context and Background

REST, as Roy Fielding defined it, is an architectural style, not a protocol. In practice “REST API” now means resource-oriented URLs, HTTP methods with their registered semantics, JSON bodies and status codes, usually described by an OpenAPI document. Its main asset is that it uses HTTP as an application protocol, so every cache, proxy, gateway and browser already understands it. HTTP semantics are consolidated in RFC 9110, caching in RFC 9111, and the transports in RFC 9112 (HTTP/1.1), RFC 9113 (HTTP/2) and RFC 9114 (HTTP/3).

gRPC came out of Google’s internal Stubby system and was open-sourced in 2015. It uses HTTP/2 as a transport tunnel for a remote procedure call model: you define services and messages in Protocol Buffers, generate stubs, and call methods. HTTP/2 is not incidental to this design. gRPC depends on multiplexed streams, flow control and, crucially, HTTP trailers to deliver the final status of a call.

GraphQL, created at Facebook and now stewarded by the GraphQL Foundation, is a query language and execution model. A client sends a document describing the shape of data it wants, and the server resolves each field. The wire protocol is almost an afterthought; the value is in the typed schema and the client-driven selection set. Its formal specification only gained a new edition in September 2025, the first since October 2021, with OneOf input objects, schema coordinates and broader deprecation support.

Connect, from Buf, answers a narrower question: how do you keep Protobuf schemas and generated code while giving up the requirement for special proxies? It was accepted into the CNCF at Sandbox level on April 13, 2024. A Connect server handler can serve the Connect protocol, gRPC and gRPC-Web from the same implementation, so one service can be called by curl, a browser and a native gRPC client.

If you are building the API description layer, the OpenAPI and Swagger tooling guide covers the REST side, and the AsyncAPI 3 guide covers the event-driven counterpart. For authoritative reference on the transport differences discussed below, see the gRPC over HTTP/2 protocol document and the Connect protocol specification.

The Core Argument: You Are Choosing a Contract Model, Not a Wire Format

The four options sit on two independent axes. The first is who defines the shape of a response: the server (REST, gRPC, Connect) or the client (GraphQL). The second is how much of the exchange is visible to generic HTTP infrastructure: a lot (REST, Connect unary, GraphQL over GET), or little (gRPC, where status lives in trailers and every call is a POST to a method path).

Thinking along those axes explains almost every production surprise. Wire size and serialization speed are a third-order concern that matters mainly for high-volume service-to-service calls.

gRPC vs REST vs GraphQL vs Connect decision flow by caller type, cacheability and streaming needs

Figure 1: A decision flow for gRPC vs REST vs GraphQL vs Connect. The first question is who the caller is, not which format is fastest.

The diagram reads from the caller downward. Browsers and partners pull toward REST or Connect because they need no special client runtime. Internal services with streaming or deadline requirements pull toward gRPC. Many divergent client shapes over many backends pull toward a GraphQL gateway. Connect is the default when you want a typed Protobuf contract but cannot guarantee HTTP/2 end to end.

Direct answer: use REST for public, cacheable, resource-shaped APIs; gRPC for internal service-to-service calls with streaming and deadline propagation; GraphQL as a client-facing aggregation layer over many services; and Connect when you want gRPC’s schema discipline with browser and curl compatibility. Most mature systems use two of them, not one.

Contract ownership shapes everything downstream

With REST, the contract is usually written after the fact. An OpenAPI document is generated from code or hand-authored, and clients are generated from it or written by hand. Because the artifact is optional, drift is the normal failure. Teams that treat OpenAPI as the source of truth and lint it in CI avoid this, but nothing in HTTP forces them to.

With gRPC and Connect, the .proto file is the contract and the code generator is mandatory. Server interfaces and client stubs come out of the same source, so a field rename breaks compilation on both sides before anything ships. The cost is a build step and a toolchain, which matters more for small public APIs than for a platform team.

With GraphQL, the schema is the contract and it is introspectable at runtime. The twist is that clients select fields, so removing a field is a breaking change you cannot detect from the server alone. This is why the September 2025 edition’s expanded deprecation support matters: the ecosystem has been compensating for weak deprecation semantics with usage tracking in gateways.

Where errors live

REST puts errors in HTTP status codes plus a body. RFC 9457 defines a standard problem-details JSON shape, and following it gives clients a consistent structure. The weakness is that the 4xx and 5xx vocabulary is coarse, and teams invent private error envelopes.

gRPC separates transport success from call outcome. A call that fails is still an HTTP 200 on the wire, and the real outcome is in the grpc-status trailer, one of 17 codes. That is elegant for RPC semantics, but generic observability sees a wall of 200s unless it understands gRPC. Connect goes the other way: unary errors use real HTTP status codes with a JSON error body containing a code, message and optional details, and each Connect code maps to a defined HTTP status.

GraphQL typically returns HTTP 200 with an errors array alongside partial data. The partial-success model is useful to UIs but breaks monitoring that keys on status codes. The in-progress GraphQL over HTTP specification defines the application/graphql-response+json media type, under which request-level failures can use non-200 statuses, but it is still a working draft rather than a finished standard.

Visibility to intermediaries

HTTP infrastructure can only act on what it can see. A CDN can cache a REST GET using URL and Vary headers. It cannot cache a gRPC call, because the request is a POST to /package.Service/Method with a binary body. A WAF can inspect JSON for patterns; it generally cannot inspect Protobuf without a schema. A rate limiter keyed on URL works for REST and is meaningless for GraphQL, where every request hits one endpoint with a different document.

Connect narrows this gap deliberately. Side-effect-free unary methods, marked NO_SIDE_EFFECTS in the Protobuf definition, can be called with GET, with the message carried in query parameters. That makes them cacheable by ordinary HTTP caches, which is a property gRPC cannot offer. It is the clearest example of a design choice about visibility rather than performance.

Wire layout comparison of REST, gRPC and Connect showing headers, body framing and where status is reported

Figure 2: Where each protocol puts headers, payload framing and final status. gRPC relies on trailers; Connect and REST use the HTTP status line.

In Figure 2, REST and Connect report final status in the first response line, so any intermediary can classify success or failure after the first bytes arrive. gRPC defers it to trailers after the body, which is why gRPC-aware proxies are needed for correct metrics and retries. The long-description version: REST sends a request line and headers, then a text JSON body; gRPC sends an HTTP/2 HEADERS frame, then length-prefixed Protobuf messages, then trailers with the status; Connect sends ordinary HTTP with a Protobuf or JSON body and reports errors through HTTP status plus a JSON error object.

Deeper Analysis: What Actually Happens on the Wire

Payload size: a worked example, labelled illustrative

Take a user record with an integer id of 42, a name of “Alice” and an email. As JSON, the body is a few dozen to roughly a hundred bytes depending on field names and whitespace, because every field name is spelled out in every response. In Protobuf, field names never appear. Each field is a tag (field number plus wire type) followed by the value. The integer field 1 with value 42 encodes as two bytes: 0x08 for the tag and 0x2a for the varint. A string field adds a tag, a length varint and the UTF-8 bytes.

So a typical record shrinks by something like a third to a half, with the savings growing for payloads with many numeric fields, repeated fields or long field names. Those ratios are illustrative of the encoding, not measurements of your service. Repeated string-heavy data compresses well under gzip or brotli either way, which is why the gap narrows once you enable HTTP compression on JSON.

Per-message framing is small and nearly identical in the two RPC-style protocols. gRPC prefixes each message with one compressed-flag byte and a four-byte big-endian length, five bytes total. Connect’s streaming envelope uses a one-byte flags field and a four-byte length, also five bytes, with the second flag bit marking the final end-of-stream message. For unary Connect calls there is no envelope: the body is just the Protobuf or JSON message.

Headers matter more than they look for small payloads. HTTP/1.1 resends the full header block on every request. HTTP/2 uses HPACK (RFC 7541) to compress headers against a connection-scoped table, so repeated headers such as authorization tokens and user agents cost a few bytes after the first request. For a 60-byte payload, headers can dominate the HTTP/1.1 cost and nearly vanish under HTTP/2. This, not Protobuf, is where much of gRPC’s measured advantage on tiny messages comes from, and REST over HTTP/2 captures much of it too.

Latency: where the milliseconds come from

Four sources dominate. Connection setup is one: a new TCP plus TLS handshake costs one to three round trips depending on TLS version and resumption, and HTTP/2 amortizes it across many requests on one connection. Serialization is the second: parsing JSON is typically several times slower per byte than decoding Protobuf, but for payloads under a few kilobytes both are microseconds, not milliseconds. Head-of-line blocking is the third. HTTP/2 multiplexes streams over one TCP connection, so a single lost packet stalls every stream until retransmission. HTTP/3 (RFC 9114 over QUIC, RFC 9000) removes that coupling by giving each stream independent loss recovery.

The fourth is the one that decides most real systems: the work your handler does. If a handler waits 20 milliseconds on a database, whether serialization takes 5 microseconds or 50 is invisible. Protocol choice moves tail latency noticeably in only two regimes: very chatty internal call graphs with many small calls per request, and constrained links where bytes are expensive.

For IoT-flavoured traffic specifically, I compared transport behavior in IoT protocol latency across MQTT, CoAP, AMQP and HTTP/3. The same logic applies: handshake cost and loss behavior dominate, not payload encoding.

Streaming and flow control

REST has no native streaming model. Teams bolt on Server-Sent Events, chunked responses, WebSockets or long polling. OpenAPI 3.2, released in September 2025, now lets you describe streaming media types such as text/event-stream, application/jsonl and application/x-ndjson, and describe the schema of each item in the stream, which closes a documentation gap but does not add bidirectional semantics.

gRPC defines four call shapes: unary, server streaming, client streaming and bidirectional streaming. Each message is length-prefixed on one HTTP/2 stream, and HTTP/2 flow control (per-stream and per-connection window updates) gives you backpressure for free: a slow consumer shrinks the window and the producer’s writes block. That is a real advantage over hand-rolled NDJSON, where backpressure depends on how well your framework propagates TCP pressure to application code.

Connect supports all four shapes at the API level, with a crucial constraint. Unary and server or client streaming work over HTTP/1.1, because the request can be sent in full or the response streamed as chunks. Bidirectional streaming requires HTTP/2, since both sides must write concurrently on one exchange. Browsers add another limit: fetch request streaming is not universally available and is effectively half-duplex, so browser clients usually get unary and server streaming, not full bidirectional calls.

GraphQL streams through subscriptions, typically over WebSockets or SSE, and through incremental delivery with @defer and @stream. Notably, incremental delivery was not part of the September 2025 specification edition, so support depends on the server library and remains a moving target.

Deadlines and cancellation

A distributed call chain needs a shared budget. gRPC carries it in the grpc-timeout header: a positive integer of at most eight digits with a unit suffix (H, M, S, m, u, n). Libraries convert that to an absolute deadline in context, subtract elapsed time for each downstream call, and cancel work when it expires. Connect expresses the same idea with the connect-timeout-ms header, an integer of up to ten digits.

The practical effect is that when service A has 300 milliseconds left and calls B, B sees roughly 300 minus whatever A already spent. An overloaded dependency cannot accumulate unbounded queued work for callers who have already given up. In REST you can build the same thing with a custom header, but you then need every service and client to honor it, and most do not.

GraphQL execution and the N+1 trap

GraphQL’s flexibility has a mechanical cost. The server parses the document into a syntax tree, validates it against the schema, then executes the selection set by calling a resolver per field. A naive resolver for posts on each user issues one database query per user. Fetching 100 users and their posts results in 1 query for users plus 100 for posts, which is the N+1 problem.

The standard mitigation is batching within a request tick, as in the DataLoader pattern: collect all requested user ids during one level of execution, then issue one WHERE user_id IN (...) query. In the illustrative case, 101 queries become 2. The same idea applies in a federated gateway, where the planner batches entity lookups across subgraphs.

GraphQL gateway sequence showing one client query fanned out to users and posts services with batched fetches

Figure 3: A GraphQL gateway resolving a nested query. The planner fetches users once, then batches post lookups by id instead of issuing one call per user.

Figure 3 shows the efficient path. The client sends one document; the gateway parses, validates and plans it; it calls the users service once; then it issues a single batched call to the posts service keyed by the returned ids. Without batching, the gateway would call the posts service 100 times, and the round-trip count would scale with result size.

Federation splits the supergraph across teams. Apollo Federation is the established implementation, and the GraphQL Foundation’s Composite Schemas working group is drafting a vendor-neutral specification for composition and distributed execution. As of this writing it is at the proposal stage, so treat portability between gateways as unproven.

Schema evolution rules

Protobuf’s compatibility rules are strict and well understood. Adding a field with a new number is safe. Removing a field is safe only if you reserve its number and name so nobody reuses them. Changing a field’s type or reusing a number silently corrupts data. Tools such as buf breaking check these rules in CI against a previous version of the schema, which turns evolution into a mechanical check.

JSON APIs rely on conventions: add fields, never repurpose them, and version in the path or a header when you must break. Clients that ignore unknown fields tolerate additions, but nothing enforces that. GraphQL evolves by adding fields and deprecating old ones, relying on usage analytics to decide when removal is safe.

A subtle point: Protobuf’s JSON mapping, used by Connect’s JSON mode, uses field names and defines specific rules for 64-bit integers (serialized as strings) and well-known types. If you switch a client between binary and JSON, test those edges, because JavaScript numbers lose precision above 2^53.

Caching and conditional requests

REST’s strongest property is cacheability. A GET with Cache-Control, ETag and Vary lets a CDN serve repeated reads without touching your origin, and conditional requests turn many reads into small 304 responses. This is difficult to replicate anywhere else.

GraphQL queries are commonly sent as POST because documents are long, which makes them uncacheable by default. Persisted queries address this: clients send a hash, the server maps it to a stored document, and the hash can travel in a GET URL. That regains CDN caching at the cost of a registry and a deploy step.

RFC 10008 changes the landscape slightly. The QUERY method is safe and idempotent, allows a request body, and defines caching in terms of the body content. It also introduces an Accept-Query response header so servers can advertise supported query media types. For GraphQL-style and search-style reads, QUERY is the semantically correct verb that POST never was. Adoption will depend on browsers, CDNs and frameworks catching up, so in October 2026 treat it as a verb to design toward, not to rely on across arbitrary intermediaries.

Trade-offs, Gotchas, and What Goes Wrong

gRPC and the proxy problem. Native gRPC requires an end-to-end HTTP/2 path that preserves trailers. Any component that downgrades to HTTP/1.1, buffers entire responses or strips trailers breaks calls in confusing ways. Corporate TLS-inspection middleboxes are a classic offender, and older load balancers that terminate HTTP/2 and speak HTTP/1.1 upstream cannot forward gRPC at all. Connect or gRPC-Web at the edge avoids most of this.

gRPC load balancing. Because HTTP/2 multiplexes many calls over one long-lived connection, connection-level L4 load balancing sends all traffic from one client to one backend. You need L7, request-level balancing: a gRPC-aware proxy, a service mesh or client-side load balancing with name resolution. This surprises teams moving from REST, where a new connection per few requests spreads load incidentally. In Kubernetes, the Gateway API versus Ingress comparison is relevant, because Gateway API has a dedicated GRPCRoute resource for this case.

Browsers and gRPC. Browsers cannot issue native gRPC calls. JavaScript fetch does not expose HTTP/2 framing and does not give access to trailers, so gRPC-Web moves the trailers into the body and needs a translating proxy such as Envoy. Connect avoids the translation layer, since a Connect server can speak the browser-friendly protocol directly.

GraphQL denial of service. A client controls query shape, so a hostile or merely careless client can request deeply nested or wide selections. Mitigations are layered: depth limits, complexity scoring, pagination caps on every list field, timeouts, persisted-query allow-lists for public clients, and per-field cost accounting at the gateway. Do not rely on rate limiting per request, because one request can be arbitrarily expensive.

Connect’s smaller ecosystem. Connect is a CNCF Sandbox project, which signals early maturity rather than deep production adoption. Official implementations exist for Go, TypeScript on web and Node.js, Python, Kotlin, Swift and Dart, but tooling such as API gateways, WAFs and observability products has deeper native support for gRPC and REST. Check your gateway before committing.

REST’s quiet costs. The ambiguity of REST is also its tax. Is it PUT or PATCH? Which status code for validation failure? How are partial updates and bulk operations represented? Without a style guide and linting, every endpoint becomes a negotiation. Retries add another trap: POST is not idempotent by default, so clients need idempotency keys. The idempotent API design guide covers this in detail, and it applies to gRPC and Connect mutations equally.

Protobuf JSON versus binary. Binary Protobuf is opaque in logs and browser dev tools. Connect’s JSON mode and gRPC’s +json content type help debugging, but you pay in size and the integer edge cases above. Decide which is the production default and test the other.

Deployment topology with browser, mobile and device clients reaching gRPC and Connect services through an edge proxy with propagated deadlines

Figure 4: A mixed-protocol topology. The edge translates for browsers, internal calls use gRPC or Connect over HTTP/2 with propagated deadlines, and devices use lighter protocols bridged at the boundary.

Figure 4 puts the pieces together. Browsers reach the edge using Connect or gRPC-Web, mobile apps can use native gRPC over HTTP/2, and device fleets arrive over HTTP or an MQTT bridge. The edge proxy forwards over HTTP/2 to services that implement gRPC or Connect handlers, and each service passes a shrinking deadline to its dependencies. The datastore at the end is where the budget is usually spent.

Decision Matrix

The matrix below summarizes the properties that matter for the choice. Ratings describe typical behavior, not hard limits.

Property REST gRPC GraphQL Connect
Contract source OpenAPI, optional .proto, mandatory SDL schema, introspectable .proto, mandatory
Payload JSON, text Protobuf, binary JSON, text Protobuf or JSON
Transport requirement HTTP/1.1, 2 or 3 HTTP/2 with trailers Any HTTP HTTP/1.1 and 2, bidi needs 2
Native browser call Yes No, needs gRPC-Web Yes Yes
Streaming SSE, NDJSON, add-ons All four shapes Subscriptions, defer All four shapes, bidi needs HTTP/2
HTTP cache friendliness High None Low, improves with persisted GET Medium, GET for side-effect-free calls
Error visibility to proxies HTTP status Trailers, status 200 Usually 200 with errors array HTTP status plus JSON error
Deadline propagation Manual Built in Manual Built in
Schema break detection Lint tools buf breaking, strict rules Usage-based, gateway tooling buf breaking, strict rules
Over and under-fetching Common Method-defined Client chooses fields Method-defined
Public API fit Excellent Poor Good with guardrails Good
Internal service fit Fine Excellent Poor as service transport Excellent

The use-case view is clearer than the feature view. For a public developer API consumed by third parties in many languages, REST with OpenAPI is hard to beat, because the audience values curl, caching and familiar tooling over compactness. For an internal platform of Go and Java services with long-running streams and strict latency budgets, gRPC is the natural fit. For a product with web, iOS and Android clients that each need different slices of the same backend data, a GraphQL layer reduces round trips and backend-for-frontend sprawl. For a team that wants typed contracts end to end including a TypeScript frontend and no translation proxy, Connect removes the most operational friction.

Browser, Mobile and IoT Considerations

Browsers

Browsers are the hardest client for gRPC and the easiest for REST. Fetch gives you request and response bodies but not trailers or framing, so gRPC-Web adapts the protocol: trailers are encoded as a final frame in the body. Connect’s own protocol is designed around what fetch can do, which is why it works in browsers without a translating proxy. For unary calls, a Connect request is a plain POST with Content-Type: application/json or application/proto, and the connect-protocol-version: 1 header marks it as Connect traffic.

If you already run a gRPC backend and cannot change it, the question is whether to add Envoy for gRPC-Web translation or rewrite handlers with Connect. Connect handlers also serve gRPC and gRPC-Web, so migrating service by service is feasible without breaking existing clients.

Mobile

Native gRPC works well on mobile because the client controls its HTTP/2 stack and long-lived connections reduce handshake cost on flaky networks. The caveats are radio behavior and connection migration. A cellular handoff breaks a TCP connection, so the client pays a reconnect. HTTP/3 over QUIC supports connection migration, which is attractive here, but gRPC over HTTP/3 remains a proposal under the gRPC project rather than a settled capability across implementations, so verify what your language library actually supports before designing around it.

Payload size matters more on mobile than on server links, which favors Protobuf for chatty features. For read-heavy catalog screens, REST with strong HTTP caching and CDN offload may beat any RPC protocol, because the best request is the one that never reaches your origin.

IoT and constrained devices

Constrained devices rarely speak gRPC. Microcontrollers with kilobytes of RAM use MQTT or CoAP, and cloud gateways translate to service protocols. gRPC is a fit for edge gateways and Linux-class devices with a proper TLS stack and enough memory for HTTP/2 state. If your device fleet includes both, a pattern that works well is MQTT or HTTP at the device boundary, a bridge service that normalizes messages, and gRPC or Connect inside the platform. The same shape appears in digital twin ingestion pipelines, where the device-facing protocol is chosen for power and link constraints while the internal contract is chosen for schema discipline.

OpenAPI 3.2’s explicit support for the OAuth device authorization flow is a small but relevant improvement for CLIs and smart devices that need to authenticate without a browser.

Security and identity

Transport security is the same across all four: TLS 1.2 or 1.3, with gRPC and HTTP/3 effectively mandating encryption in practice (HTTP/3 requires TLS 1.3 by design). The differences are in what you can inspect. REST and JSON-based GraphQL can be filtered by WAF rules; Protobuf bodies need schema-aware inspection or inspection at the service. For workload identity inside a cluster, mutual TLS with SPIFFE identities works identically for gRPC and Connect, since both ride on HTTP/2; see the SPIFFE and SPIRE workload identity architecture for the pattern.

Authorization deserves the same scrutiny in every protocol. In GraphQL, enforce authorization in resolvers or a gateway policy layer, per field and per object, never only at the endpoint. In gRPC and Connect, use interceptors or middleware to verify identity and per-method permissions uniformly. Field-level exposure in GraphQL and reflection endpoints in gRPC both make attack surface discoverable, so disable introspection and reflection for untrusted callers where appropriate. A related set of concerns for tool-calling endpoints is covered in the MCP server security architecture article.

Practical Recommendations

Start from the boundary, not the technology. For each interface in your system, name the caller, the cacheability of the data, whether it streams and who controls deployment of both ends. Those four facts almost always point to one answer.

Use one protocol per boundary class. A sensible default in 2026 is REST with OpenAPI for external and partner APIs, Connect or gRPC for internal service calls, and GraphQL only where multiple client types genuinely need flexible selection over several backends. Resist GraphQL as an internal service transport; the resolver fan-out and weak per-field observability cost more than they return between services you control.

If you adopt gRPC, plan the infrastructure first: HTTP/2 end to end, L7 load balancing, health checking, and an edge story for browsers. If you adopt Connect, keep the Protobuf discipline and run buf breaking in CI. If you adopt GraphQL, put limits in on day one, not after the first incident.

Checklist:

  • Name the caller and the cache requirement for each API before choosing a protocol.
  • Keep a single source of truth for the contract: OpenAPI, .proto or SDL, linted in CI.
  • Run breaking-change detection against the last released schema.
  • Propagate deadlines on every internal call and set a default at the edge.
  • Make errors observable: map gRPC status and GraphQL errors into metrics, not just HTTP codes.
  • For GraphQL, enforce depth, complexity and pagination limits, and use persisted queries for public clients.
  • Verify the whole path supports HTTP/2 and trailers before shipping gRPC.
  • Use idempotency keys for non-idempotent mutations in every protocol.
  • Measure with your own payloads and call graph before claiming a protocol saves latency.

Frequently Asked Questions

Is gRPC faster than REST?

Usually, for small messages and chatty internal call graphs, but the gap is smaller than headlines suggest. Gains come from HTTP/2 connection reuse, HPACK header compression, binary Protobuf encoding and cheaper parsing. REST over HTTP/2 with compression captures part of the benefit. If your handlers wait on databases for tens of milliseconds, the protocol difference disappears into noise. Measure with your own payload sizes and call patterns, not published synthetic results.

Can browsers call gRPC directly?

Not natively. Browser JavaScript cannot control HTTP/2 framing or read HTTP trailers, both of which gRPC depends on. The workarounds are gRPC-Web, which needs a translating proxy such as Envoy, or Connect, whose protocol is designed around what the Fetch API can do and works without a proxy. Connect servers also accept gRPC and gRPC-Web, so one handler can serve browsers and native clients.

What is the difference between Connect and gRPC-Web?

gRPC-Web is a browser-compatible variant of gRPC that still requires a proxy to translate to native gRPC on the backend and uses binary framing. Connect is a family of libraries with its own HTTP-based protocol that is readable in browser dev tools in JSON mode, supports GET for side-effect-free calls, and requires no translating proxy. Connect implementations also speak gRPC and gRPC-Web, so adopting it does not strand existing clients.

Is GraphQL replacing REST?

No. GraphQL solves a specific problem, client-driven aggregation over several backends, and carries costs in caching, authorization granularity and query-cost control. REST remains the default for public and cacheable APIs, and the two commonly coexist, with GraphQL as a gateway in front of REST or gRPC services. The September 2025 specification edition shows the standard is evolving, but adoption patterns are additive, not substitutive.

What is the HTTP QUERY method and should I use it?

QUERY, published as RFC 10008 in June 2026, is a safe and idempotent method that allows a request body, with defined caching semantics. It fits complex searches and GraphQL-style reads better than POST. Use it where you control both ends or have verified intermediary support, and keep a POST or GET fallback for the near term, since browsers, CDNs and frameworks are still catching up.

Does gRPC work over HTTP/3?

Not as a settled, broadly supported standard yet. gRPC over HTTP/3 exists as a proposal in the gRPC project, which addresses negotiation and mandatory TLS, but language implementations vary and many production deployments still run HTTP/2. Connect and REST can run over HTTP/3 wherever your server and proxy stack support it. Check your specific library and load balancer before relying on QUIC features.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *