Calling another service becomes more involved when the two teams use different languages. Suppose a checkout service that uses Go needs inventory information from a service that uses Java before accepting an order. Both teams need to agree on the lookup operation, the request fields, the response, and the meaning of failure. Handwriting request-building and parsing code in each language adds work without resolving those design decisions.
gRPC provides a framework for defining remote operations and connecting clients to their implementations.
This chapter explains its programming model, the role of generated code, what happens during a call, and the practical considerations that determine whether it fits an API.
A remote procedure call (RPC) asks another process to execute a named operation. The client supplies input, and the remote service performs the work and returns a result or reports a failure.
For the checkout service, the operation might be GetAvailability. Its inputs identify a product and a warehouse. Its result tells checkout how many units are currently available there.
With a resource-oriented HTTP API, a designer starts by modeling resources and choosing HTTP methods. With an RPC interface, the designer exposes service methods with request and response types. Both approaches still require clear behavior, authorization, and compatibility rules.
The appeal of RPC is that calling a remote operation can resemble calling a method in the client's language. That resemblance has a limit: the operation crosses a process boundary. The inventory service might be unavailable, the network might fail, or the result might arrive too late to help checkout.
A local-looking call therefore needs an explicit waiting limit and failure handling. Generated code cannot make a remote dependency behave like an ordinary in-memory function.
gRPC is an open-source RPC framework with support for multiple programming languages. In a typical setup, teams describe service methods and messages using Protocol Buffers, also called Protobuf. Native gRPC uses HTTP/2 to carry calls between client and server.
These pieces have different responsibilities:
Serialization converts a message into bytes for transmission or storage. Deserialization reconstructs a message from those bytes. Protobuf is gRPC's default choice for this job, rather than a synonym for gRPC itself. Applications can use Protobuf independently, and gRPC can support other serialization choices.
The definition normally lives in a .proto file. A common build workflow uses the Protobuf compiler, protoc, with a language-specific gRPC plugin. This produces message code and the client and server interfaces needed for that language.
The diagram shows how two implementations use one shared definition during development:
The generated client, often called a stub, exposes methods the application can call. The server team supplies the business logic behind its generated interface. Code generation removes repetitive communication code; it does not implement the inventory calculation.
Each team can build and deploy independently, provided the versions they use remain compatible. Sharing a definition does not require sharing an implementation language or releasing both services together.
Assume this inventory API serves authenticated internal services over TLS, which encrypts the connection and supports peer identity verification. Callers must have permission to read the requested warehouse. The lookup reports a current observation of stock; it does not reserve units.
Here is a complete, language-neutral schema for that operation:
The service contains a named rpc method. Its input and output are message types, each containing typed fields. The numbers such as = 1 are field identifiers that Protobuf uses, not default values or validation rules. inventory.v1 is this API's chosen package name; including a version in it is a convention, not a requirement that gRPC imposes.
The comments carry meaning that the field types alone cannot express. A string can be empty, and an int32 can be negative. The application must reject empty identifiers and ensure that successful quantities follow the documented non-negative rule. Under this proto3 definition, the generated message API reads an omitted request string as an empty string, so the same validation rejects both cases.
Consider this concrete call, shown as logical message values rather than a wire-format dump:
For this example, the service applies the following behavior. These are API policy choices using standard gRPC statuses:
The server authenticates the caller before handling the lookup and checks warehouse access before exposing record existence. Failure results do not include a successful availability response. In particular, do not report zero stock when the inventory service is unavailable: “we could not read inventory” and “inventory is empty” require different decisions from checkout.
Even a successful lookup cannot guarantee that the stock will still be available. Another order can consume those twelve units after the lookup. If checkout needs guaranteed stock, provide a separate reservation operation that handles competing requests safely. Changing the transport cannot create that guarantee.
Before making calls, an application creates a channel, the client-side abstraction for communicating with a service target. The client stub uses that channel. A channel manages connections; it does not promise to use one permanent network socket. Generally, reuse channels instead of recreating them for each lookup.
For a call, the client supplies the request and can attach metadata, key-value information associated with the call, such as credentials or tracing context. It should also set a deadline, the point after which the result is no longer useful to the caller.
This diagram follows a successful lookup and simplifies the transport steps to emphasize responsibilities:
Application code owns the inventory behavior and access decision. The runtime carries messages between the applications. Depending on the language and API used, the client can wait synchronously or use an asynchronous programming interface; a remote call does not inherently require blocking a thread.
At the HTTP/2 layer, native gRPC calls use POST and a path identifying the service and method. For this example, the path is /inventory.v1.InventoryService/GetAvailability. A lookup still uses POST even though its application behavior is read-only.
Messages use gRPC framing around their serialized bytes. A normal response ends with a gRPC status in trailing headers, called trailers. An HTTP 200 alone does not establish that the operation succeeded: the gRPC status may report an application error. Monitoring therefore needs the RPC outcome, not just the HTTP status.
Failures can also prevent a normal response from arriving. If checkout reaches its deadline, it may observe DEADLINE_EXCEEDED even if inventory finished processing. For an operation that changes state, losing the response does not prove that the change never happened. Cancellation likewise does not roll back completed changes. Retry decisions must account for the operation's semantics.
HTTP/2 allows multiple streams to share a connection. In native gRPC, each RPC uses an HTTP/2 stream, so several calls can be in progress on the same connection without each needing a separate connection.
The diagram shows independent lookup calls sharing one connection; each arrow represents a separate RPC stream:
Connection sharing reduces the need for separate connections, but it does not remove capacity limits. Calls still compete for server resources and network bandwidth. A slow inventory database can dominate latency regardless of the message format.
An HTTP/2 stream is also distinct from a streaming RPC. GetAvailability is a unary RPC: one request and one response on success. gRPC additionally supports server streaming, where one request produces a sequence of response messages; client streaming, where a sequence of requests produces one response; and bidirectional streaming, where both sides send sequences of messages.
These patterns are useful when the application naturally exchanges multiple messages, but they do not turn gRPC into a durable event broker. Replaying missed updates and surviving disconnected consumers require explicit application or storage design.
Compact binary messages, generated serialization code, and connection reuse can help efficiency. They do not establish a universal performance ranking. Measure representative payloads, concurrency, and end-to-end latency before choosing gRPC for a performance target. REST APIs can also use HTTP/2, so comparing gRPC with REST is not simply comparing HTTP versions.
gRPC is a useful candidate when service teams can adopt a shared contract and supported tooling, especially across implementation languages. For checkout and inventory, its value is a consistent operation definition and generated integration code on both sides.
That workflow introduces maintenance obligations. Teams need reproducible code generation, compatible schema changes, and a way to distribute updated definitions or generated packages. A successful build against a new schema does not prove that an older deployed server supports the new method.
Client environments matter too. Ordinary browser networking APIs do not expose everything a native gRPC client needs. Browser applications commonly use gRPC-Web, a browser-oriented protocol with a compatible server or translating proxy, or an HTTP gateway. Check which streaming patterns the selected browser stack supports; do not assume it matches native gRPC.
Operational tools also need to understand the protocol. Binary payloads are less convenient to inspect manually than JSON text. Debugging tools need the message definitions, whether you supply definition files or the tools discover them through a configured schema-discovery service. Load balancers and proxies must support the intended gRPC traffic, including trailers and any long-lived calls.
Likewise, a read-only gRPC method does not automatically gain the behavior of an HTTP GET in a shared cache or CDN. If broad browser access, simple manual requests, or standard HTTP caching is central to the product, a resource-oriented HTTP interface may be easier to operate. A system can expose such an interface to consumers while using gRPC between internal services.
Finally, “internal” describes an audience, not a trust guarantee. Configure transport security and caller authentication, enforce permissions in the service, and record useful outcomes without logging sensitive message contents. The framework provides integration mechanisms; the API still needs deliberate policies.
gRPC organizes communication around named service methods and typed messages. A shared definition can generate client and server code in different languages, while the runtime handles the call mechanics over HTTP/2. Protocol Buffers supplies the default message format.
The method-call interface still crosses a network. Validation, authorization, deadlines, failure handling, and business guarantees remain part of API design. Choose gRPC when its contract workflow and communication patterns fit the consumers, infrastructure, and operational needs of the service.