In Part 2, we built four federated subgraphs across three languages. Every subgraph speaks GraphQL to the Apollo Router. But internally, not every service boundary benefits from GraphQL's flexibility.
The Product Catalog needs real-time stock counts from the Inventory service for every product listing. Sending a GraphQL query for each stock check would mean HTTP/1.1 overhead, JSON parsing, and schema validation for what is fundamentally a key-value lookup. Instead, the two Java services communicate over gRPC — binary protobuf over HTTP/2, with connection multiplexing and zero-copy deserialization.
Meanwhile, the Order Service processes payments through Stripe's REST API. Stripe doesn't offer a GraphQL endpoint, and wrapping a REST client in a GraphQL layer would add complexity without benefit.
This article examines both protocol boundaries and the reasoning behind each choice.
Protocol Selection by Use Case
Three protocols serve three different purposes. GraphQL faces the client. gRPC connects internal Java services. REST integrates with Stripe's external API.
The decision matrix is straightforward:
| Boundary | Protocol | Why |
|---|---|---|
| Client → Platform | GraphQL | Flexible queries, strong typing, client-driven data fetching |
| Product Catalog ↔ Inventory | gRPC | Internal, high-frequency, latency-sensitive, same-language (JVM) |
| Order Service → Stripe | REST | Third-party API, no alternative protocol available |
| Router ↔ Subgraphs | GraphQL | Federation protocol requirement |
gRPC: Product Catalog to Inventory
The Case for gRPC
When a client queries products with inventory data, the Product Catalog subgraph can resolve inventory through two paths:
-
Through the router: Return a
Productentity stub, let the router fetch inventory from the Inventory subgraph via federation. This works but adds a network hop through the router for every product. -
Direct gRPC call: The Product Catalog calls the Inventory service directly over gRPC, bypassing the router entirely. This is faster for batch lookups and avoids the query planning overhead for a simple key-value fetch.
The platform uses path 2 for the gRPC connection, while path 1 remains available through federation for queries that originate from other subgraphs.
Protobuf Definition
The service contract lives in a shared proto file:
Five RPCs cover the full inventory lifecycle. GetInventoryBatch is critical — when the Product Catalog resolves a list of 20 products, it makes one gRPC call to fetch all 20 inventory records instead of 20 individual calls.
gRPC Server (Inventory Service)
The Inventory service implements the gRPC server using Micronaut's gRPC support:
The gRPC server runs on port 50051, separate from the HTTP/GraphQL endpoint on port 4004. This is intentional — the gRPC port is internal-only (not exposed outside the Docker network), while the GraphQL port is accessible to the router.
gRPC Client (Product Catalog)
The Product Catalog consumes inventory data through a gRPC client managed by Micronaut's dependency injection:
Micronaut's @GrpcChannel("inventory") annotation resolves the channel configuration from application.yml:
Why gRPC Over REST for This Boundary
The inventory lookup is a hot path — called for every product list, product detail page, and search result. The performance characteristics of gRPC matter here. Binary serialization with Protobuf produces messages 3-10x smaller than equivalent JSON. HTTP/2 multiplexing lets multiple concurrent requests share a single TCP connection. Streaming support means GetInventoryBatch sends one request and receives one response with all items. Code generation from the same .proto file eliminates serialization bugs on both client and server. And connection management through Micronaut handles the gRPC channel lifecycle, including reconnection and load balancing.
For a boundary between two JVM services on the same Docker network, these advantages compound. The overhead of REST (JSON serialization, HTTP/1.1 connection management, no type safety) would be unnecessary friction.
REST: Order Service to Stripe
The Stripe Integration
On the other end of the protocol spectrum, the Order Service integrates with Stripe for payment processing. Stripe provides a REST API and official SDKs. There's no protobuf definition to share, no internal service to control — this is a third-party integration.
Order Creation Flow
When a client sends a createOrder mutation, the resolver orchestrates database writes and Stripe calls:
Why REST Is the Right Choice Here
Using REST for Stripe isn't a compromise — it's the only sensible option. Stripe's API is REST-only with no gRPC or GraphQL endpoint available. The official SDK (stripe-go) handles authentication, retries, idempotency keys, and webhook signature verification. The low frequency of payment operations — once per checkout, not per page load — makes serialization overhead negligible. And the error semantics of Stripe's error codes and HTTP status codes map cleanly to GraphQL error responses.
Wrapping Stripe in a gRPC service would add a translation layer between two different error models. Wrapping it in a GraphQL subgraph would be worse — federation entity resolution doesn't map to payment intent creation.
The Dual Protocol Pattern in Java
The Inventory service is the most interesting case because it serves both protocols simultaneously:
The Inventory service exposes two entry points — GraphQL for the federation router and gRPC for the Product Catalog — both delegating to the same business logic layer.
Both the GraphQL data fetchers and the gRPC service handlers call the same InventoryService class. The transport protocol is an adapter; the domain logic is shared. This avoids duplicating business rules across protocol boundaries while letting each caller use the most appropriate protocol.
Looking Forward
With the subgraphs built and the internal protocols wired, the remaining piece is the gateway layer that makes it all look like a single API. Kong validates JWTs and enforces rate limits. Apollo Router composes the supergraph and plans queries across subgraph boundaries.
In Part 4, we'll examine how these two gateways interact, how the router builds query plans for cross-service queries, and how authentication context flows from the browser through Kong to every subgraph.
This article is part of the Polyglot GraphQL Federation series. Continue to Part 4: The Gateway Layer to see how Kong and Apollo Router compose a secure, federated API.
