# gRPC — Part 1: Protocol Buffers, Contracts, and Unary Calls

This topic assumes the REST model from earlier in this group and the GraphQL model from `12-graphql`. gRPC is a third distinct approach: instead of REST's resource-oriented URLs or GraphQL's client-specified query language, gRPC exposes strongly-typed **remote procedure calls** — the client calls what looks like a local function, and the framework handles serialization and network transport. This part covers Protocol Buffers, the strict contract they enforce, and simple request/response (unary) calls. Part 2 covers streaming modes and when to actually choose gRPC over REST/GraphQL.

## 1. Protocol Buffers: the Contract, in a `.proto` File

Every gRPC service is defined in a `.proto` file — both the data shapes AND the callable methods, compiled into strongly-typed client/server code for whichever language you target:

```protobuf
syntax = "proto3";

package orders;

message Order {
  string id = 1;
  string status = 2;
  int64 total_cents = 3;
}

message GetOrderRequest {
  string id = 1;
}

service OrderService {
  rpc GetOrder(GetOrderRequest) returns (Order);
}
```

The numbers after each field (`= 1`, `= 2`, `= 3`) are **field tags** — they identify the field on the wire, not the field's position or name. This is central to Protocol Buffers' wire format (see Under the Hood-style detail in section 3) and to how the format stays backward-compatible as a schema evolves.

## 2. Codegen: Client and Server Stubs From One Source of Truth

A `.proto` file is compiled (via `protoc` or a language-specific plugin) into real client and server code — this is the single most distinctive difference from both REST and GraphQL, where the schema documents the contract but doesn't directly generate the calling code:

```ts
// Generated client stub (conceptually — real codegen output is more verbose)
const client = new OrderServiceClient("orders.internal:50051");
const order: Order = await client.getOrder({ id: "482" }); // looks like calling a local async function
```

```ts
// Generated server-side interface the implementation must satisfy
class OrderServiceImpl implements OrderServiceServer {
  async getOrder(request: GetOrderRequest): Promise<Order> {
    return db.orders.findById(request.id);
  }
}
```

Both sides are generated from the *same* `.proto` file, which means a mismatch between what the client sends and what the server expects is caught by the type system at compile time, in both languages — not discovered at runtime the way a REST client sending an unexpected JSON shape would be.

![A .proto service definition is compiled into client and server stubs that exchange binary protobuf frames over HTTP/2 streams in unary, server-streaming, client-streaming or bidirectional shapes](diagrams/01-proto-stubs-http2.svg)

## 3. Binary Wire Format Over HTTP/2

Unlike REST/GraphQL's human-readable JSON over HTTP/1.1 (or HTTP/2, but typically still JSON), gRPC serializes messages into a compact **binary** format and always runs over **HTTP/2**:

```
JSON (REST/GraphQL):  {"id":"482","status":"confirmed","total_cents":5000}   -- ~50 bytes, human-readable
Protobuf (gRPC):       0x0a 0x03 "482" 0x12 0x09 "confirmed" 0x18 0x88 0x27   -- ~20 bytes, NOT human-readable
```

Each field is encoded as `(tag, wire-type, value)` — the tag from section 1 identifying which field it is on the wire, without ever transmitting the field's *name* (`"status"` as a string) the way JSON does. This is smaller and faster to parse than JSON, at the direct cost of no longer being readable with `curl` and your eyes — inspecting gRPC traffic requires a protobuf-aware tool (like `grpcurl`) that knows the `.proto` schema to decode it.

HTTP/2 (vs. REST's typical HTTP/1.1) brings multiplexed streams over one connection (many concurrent calls without head-of-line blocking) and is a hard requirement for gRPC's streaming modes covered in Part 2 — HTTP/1.1 has no mechanism for a long-lived bidirectional stream the way HTTP/2 does.

## 4. Unary Calls: the Simple Case

A **unary** RPC is the simple one-request-one-response case — the `GetOrder` example above. It behaves like a REST GET or a GraphQL query: send a request, await exactly one response:

```ts
try {
  const order = await client.getOrder({ id: "482" });
} catch (err) {
  // gRPC errors carry a STATUS CODE from a fixed enum (OK, NOT_FOUND, INVALID_ARGUMENT,
  // PERMISSION_DENIED, UNAVAILABLE, ...) plus an optional message -- conceptually
  // parallel to HTTP status codes (backend/api-design/09-error_response_design),
  // but gRPC's set is smaller, fixed, and NOT the same numbering as HTTP status codes.
}
```

Part 2 covers the three other call shapes gRPC supports beyond unary — client streaming, server streaming, and bidirectional streaming — none of which have a clean REST or plain-HTTP-JSON equivalent.

## Common Pitfalls

1. **Reusing a field tag number for a different field after a schema change.** Because the wire format identifies fields by tag number, not name (section 3), reassigning tag `2` from `status` to something else makes old and new clients silently misinterpret each other's data — tags, once assigned, are never reused.
2. **Trying to read gRPC traffic with `curl` or a browser devtools network tab.** The binary wire format (section 3) isn't human-readable; use a protobuf-aware tool.
3. **Assuming gRPC error codes map 1:1 to HTTP status codes.** They're a separate, gRPC-specific enum — `NOT_FOUND` isn't literally the number `404` on the wire.
4. **Hand-writing client/server code instead of using codegen.** Defeats the entire point of section 2's shared-contract guarantee — a hand-written client can drift from the `.proto` file exactly the way a REST client can drift from an OpenAPI spec.

## Quick Reference

```
.proto file        -- defines messages + services, compiled into client/server stubs
Field tags (=1,=2)  -- identify fields ON THE WIRE, not by name -- never reuse after removal
Codegen              -- BOTH client and server generated from the SAME .proto -- compile-time contract
Wire format           -- binary, NOT human-readable -- smaller/faster than JSON, needs protobuf-aware tools
Transport              -- always HTTP/2 -- required for Part 2's streaming modes
Unary call               -- one request, one response -- like a REST GET or GraphQL query
Errors                    -- fixed gRPC status-code enum, separate from HTTP status codes
```

Continue to `api_grpc_theory_2.md` for streaming modes and gRPC vs. REST tradeoffs.
