ProtoPeek
Guide

Learn gRPC

A transport-first walkthrough of proto contracts, reflection, HTTP/2, metadata, gRPC-Web, and debugging pressure points.

Proto contractReflection pathsHTTP/2 and trailers
Reading path
Learn gRPC
Proto contract
Reflection paths
HTTP/2 and trailers

This page is the long-form technical companion to the ProtoPeek console. It explains the transport concepts that actually matter when you are debugging a gRPC service under pressure.

1. The contract comes first

gRPC starts with a .proto schema. Everything else — code generation, reflection, request tooling — flows from it.

syntax = "proto3";

package bookstore.v1;

service BookService {
  rpc GetBook    (GetBookRequest)    returns (Book);
  rpc ListBooks  (ListBooksRequest)  returns (stream Book);
  rpc UploadBooks(stream Book)       returns (UploadSummary);
  rpc ChatBooks  (stream ChatMsg)    returns (stream ChatMsg);
}

message GetBookRequest {
  string id = 1;
}

message Book {
  string   id     = 1;
  string   title  = 2;
  string   author = 3;
  int32    year   = 4;
  repeated string tags = 5;
}

message ListBooksRequest {
  int32  page_size  = 1;
  string page_token = 2;
}

message UploadSummary {
  int32 accepted = 1;
  int32 rejected = 2;
}

message ChatMsg {
  string sender  = 1;
  string content = 2;
}

enum BookStatus {
  BOOK_STATUS_UNSPECIFIED = 0;
  BOOK_STATUS_AVAILABLE   = 1;
  BOOK_STATUS_CHECKED_OUT = 2;
  BOOK_STATUS_RESERVED    = 3;
}

That schema matters operationally, not just for code generation. ProtoPeek relies on it to:

  • Build the method rail with streaming badges
  • Generate starter JSON payloads from reflected message types
  • Render nested request schema details
  • Decode protobuf Any values and response messages
  • Power the proto structure explorer and exporter

If the server exposes reflection, ProtoPeek discovers services at runtime. If it does not, the same information can come from proto source files or protoset files.

2. Why Protocol Buffers change the ergonomics

Compared with JSON over REST, Protocol Buffers are compact, strongly typed, and schema-driven. Field tags and binary encoding reduce payload size and parsing overhead, but the tradeoff is visible:

  • Humans cannot inspect the wire format directly the way they inspect JSON.
  • Tooling must understand descriptors to stay usable.
  • Field numbers, not field names, are the stable API surface.

That is one of the core reasons ProtoPeek exists: it translates binary descriptors back into a navigable, human-readable contract surface.

3. HTTP/2 is not a side detail

gRPC uses HTTP/2 as the transport foundation. A single gRPC call translates into HTTP/2 frames like this:

┌─────────────────────────────────────────────────────┐
│ Client                              Server          │
│                                                     │
│  HEADERS frame ──────────────────►                  │
│    :method POST                                     │
│    :path /bookstore.v1.BookService/GetBook           │
│    content-type application/grpc                    │
│    te trailers                                      │
│    grpc-timeout 15S                                 │
│    authorization Bearer <token>                     │
│                                                     │
│  DATA frame ─────────────────────►                  │
│    [5-byte header][protobuf payload]                │
│                                                     │
│                  ◄────────────────  HEADERS frame    │
│                                     :status 200     │
│                                     content-type    │
│                                     application/grpc│
│                                                     │
│                  ◄────────────────  DATA frame       │
│                                     [protobuf resp] │
│                                                     │
│                  ◄────────────────  HEADERS frame    │
│                                     (trailers)      │
│                                     grpc-status 0   │
│                                     grpc-message OK │
└─────────────────────────────────────────────────────┘

HTTP/2 provides:

  • Multiplexed streams over a single TCP connection
  • Header compression (HPACK) to reduce repeated metadata overhead
  • Bidirectional streaming — both client and server can send frames independently
  • Response trailers — metadata that arrives after the body, carrying the final gRPC status
  • Flow control — per-stream and per-connection backpressure

This means gRPC clients and tools need to expose more than just a body and a status code. Headers, trailers, deadlines, stream shape, and connection behavior all matter during real debugging.

4. The four RPC shapes

Every gRPC method is one of four streaming shapes. Here is what each looks like on the wire:

Unary (1 request → 1 response)

Client ──── Request ────► Server
Client ◄─── Response ──── Server
Client ◄─── Trailers ──── Server

The most familiar shape. One request message, one response message, then trailers with the gRPC status. Still backed by HTTP/2 metadata, deadlines, and trailers.

Server streaming (1 request → N responses)

Client ──── Request ────────────► Server
Client ◄─── Response 1 ────────── Server
Client ◄─── Response 2 ────────── Server
Client ◄─── Response 3 ────────── Server
Client ◄─── Trailers ──────────── Server

The client sends one message, the server replies with a stream. Great for feeds, event replay, progressive reads, and paginated result sets where the server keeps sending frames until done.

Client streaming (N requests → 1 response)

Client ──── Request 1 ──────────► Server
Client ──── Request 2 ──────────► Server
Client ──── Request 3 ──────────► Server
Client ──── END_STREAM ─────────► Server
Client ◄─── Response ──────────── Server
Client ◄─── Trailers ──────────── Server

The client sends a batch or live stream of messages before the server answers once. Common for file uploads, batch ingestion, and aggregation workflows.

Bidirectional streaming (N requests ↔ N responses)

Client ──── Request 1 ──────────► Server
Client ◄─── Response 1 ────────── Server
Client ──── Request 2 ──────────► Server
Client ◄─── Response 2 ────────── Server
Client ──── Request 3 ──────────► Server
Client ◄─── Response 3 ────────── Server
Client ──── END_STREAM ─────────► Server
Client ◄─── Trailers ──────────── Server

Both sides speak freely over the same HTTP/2 stream. This is the shape that breaks most "one request, one response" tooling assumptions, and why transport-aware tooling matters.

Tip
ProtoPeek shows streaming badges on every method in the sidebar rail, so you always know which shape you are working with before authoring a request.

5. Metadata and trailers are first-class

In gRPC, metadata is not an afterthought — it is part of the protocol definition.

Request metadata (headers)

HeaderPurposeExample
authorizationAuth tokenBearer eyJhbGci...
x-request-idDistributed tracingreq-7f3a-b2c1
grpc-timeoutDeadline propagation15S
x-tenant-idMulti-tenant routingtenant-acme-prod
x-feature-flagFeature gatingnew-scoring-v2

Response headers

Arrive before data frames. Carry early server context like server version, rate limit state, or cache status.

Response trailers

Arrive after all data frames. This is where the final gRPC status lives:

TrailerPurpose
grpc-statusNumeric status code (0 = OK, 14 = UNAVAILABLE, etc.)
grpc-messageHuman-readable error description
grpc-status-details-binBinary-encoded error details (rich error model)
Warning
Many generic API tools flatten or hide trailer metadata. ProtoPeek keeps it visible because real debugging often lives in the trailers, not the response body.

6. Reflection is what makes a console feel intelligent

Reflection allows a client to ask the server for its own schema at runtime:

# Discover all services
grpcurl -plaintext localhost:50051 list

# Result:
# bookstore.v1.BookService
# grpc.reflection.v1.ServerReflection
# grpc.reflection.v1alpha.ServerReflection

# Describe a service
grpcurl -plaintext localhost:50051 describe bookstore.v1.BookService

# Result:
# bookstore.v1.BookService is a service:
# service BookService {
#   rpc GetBook ( .bookstore.v1.GetBookRequest ) returns ( .bookstore.v1.Book );
#   rpc ListBooks ( .bookstore.v1.ListBooksRequest ) returns ( stream .bookstore.v1.Book );
#   ...
# }

Without reflection, a UI either needs explicit schema files from the user or it becomes blind. ProtoPeek keeps both paths:

  • Reflection for the happy path where the server cooperates
  • Proto source files for locked-down services that disable reflection
  • Protoset files for pre-compiled descriptor sets from build pipelines

7. gRPC status codes

Every gRPC call ends with a status code. Knowing which code maps to which situation saves time:

CodeNameWhen it appears
0OKSuccess
1CANCELLEDClient cancelled the call
2UNKNOWNServer threw an exception without a status
3INVALID_ARGUMENTClient sent a bad request
4DEADLINE_EXCEEDEDTimeout before server responded
5NOT_FOUNDRequested entity does not exist
6ALREADY_EXISTSCreate conflict
7PERMISSION_DENIEDAuth succeeded but the caller lacks permission
8RESOURCE_EXHAUSTEDRate limit or quota hit
9FAILED_PRECONDITIONSystem not in valid state for the request
10ABORTEDConcurrency conflict (optimistic lock failure)
11OUT_OF_RANGEOperation outside valid range
12UNIMPLEMENTEDMethod exists in the proto but server has no handler
13INTERNALServer-side bug
14UNAVAILABLEServer not ready — often transient, retry is appropriate
15DATA_LOSSUnrecoverable data issue
16UNAUTHENTICATEDMissing or invalid auth credentials
Note
UNAUTHENTICATED (16) means "who are you?" — provide credentials. PERMISSION_DENIED (7) means "I know who you are, but you cannot do this." Confusing the two is a common source of debugging frustration.

8. Why gRPC-Web exists

Native gRPC assumes capabilities that browsers do not expose: raw HTTP/2 framing, response trailers, and bidirectional streaming. Browser environments need a bridge, which is why gRPC-Web exists.

┌─────────────────────────────────────────────────────────┐
│                                                         │
│  Browser (gRPC-Web)                                     │
│    │                                                    │
│    │  HTTP/1.1 or HTTP/2 (browser-compatible)           │
│    ▼                                                    │
│  Envoy / gRPC-Web proxy                                 │
│    │                                                    │
│    │  Native gRPC over HTTP/2                           │
│    ▼                                                    │
│  Backend gRPC server                                    │
│                                                         │
└─────────────────────────────────────────────────────────┘

What changes across the bridge:

  • Trailers get packed into the response body instead of arriving as HTTP trailers
  • Only unary and server-streaming are supported — no client streaming or bidi
  • The proxy translates between wire formats, which adds latency and a failure point
  • CORS headers must be configured correctly on the proxy, not the backend

Where a problem might actually live:

SymptomLikely culprit
CORS errorsProxy config, not the gRPC service
Trailers missingProxy not forwarding, or client library not extracting
Client streaming failsgRPC-Web does not support it — use a native client
Latency spikeProxy translation overhead, not the backend

ProtoPeek's site keeps this explicit so frontend teams are not left guessing about where in the chain their bug lives.

9. Benchmarking responsibly

There is no universal "gRPC is X times faster" number worth trusting outside a specific test setup. Performance depends on too many variables:

  • Payload size — protobuf encoding is fast for small messages but the advantage narrows for large blobs
  • Compression — gzip vs zstd vs none changes throughput curves significantly
  • Streaming vs unary — stream setup cost amortizes differently under batch workloads
  • Proxy layers — each hop adds latency variance
  • TLS — handshake cost matters for short-lived connections
  • Retries and deadlines — retry storms can mask the real throughput ceiling
  • Client and server implementation — Go, Java, Rust, and C++ gRPC runtimes have different performance profiles

ProtoPeek adds value by giving you a fast way to measure your own service locally with the simulation studio. Configure runs, concurrency, and think time, then see p50, p95, p99 latency and throughput with your actual payloads. That is more useful than importing generic benchmark claims into a production discussion.

Tip
Start with ProtoPeek's "Quick pulse" preset (12 runs, 3 concurrent) for a fast sanity check, then use "Burst probe" (60 runs, 12 concurrent) to expose tail latency before real load testing.

10. Why debugging gets hard fast

The painful gRPC bugs are rarely serialization bugs. They are transport and configuration issues:

Debugging checklist

SymptomWhat to checkProtoPeek tool
No RPCs discoveredServer reflection disabled, or proto files not loadedProto structure explorer
UNAUTHENTICATED on every callMissing or malformed auth headerMetadata editor
UNAVAILABLE after deploymentTLS cert mismatch, wrong port, DNS resolutionTarget registry settings
DEADLINE_EXCEEDED under loadServer too slow, or deadline set too aggressivelySimulation studio
Works locally, fails in stagingAuthority override needed, or different TLS configTarget registry per-environment
Browser client behaves differentlygRPC-Web proxy issue, not backendTransport lens
Streaming closes unexpectedlyKeepalive timeout, proxy idle timeoutResponse lab trailers
Latency spikes at p99Concurrency bottleneck, connection pool exhaustionSimulation sparkline chart

That is why ProtoPeek combines request authoring, response inspection, metadata visibility, lightweight load probing, and transport education in one console — each of those surfaces helps diagnose a different class of gRPC issue.

11. Further reading

Official gRPC documentation:

gRPC-Web and browser integration:

Protocol Buffers:

ProtoPeek: