# gRPC Cheat Sheet

Dense quick-reference for revision and interviews. Terms in [GLOSSARY.md](./GLOSSARY.md);
our code specifics in [repo-grpc-map.md](./repo-grpc-map.md).

## The model in five lines
1. Define a **service** and its **messages** in a `.proto` — that's the contract.
2. A generator turns it into a typed **server interface** + **client stub**.
3. You implement the server; callers use the stub as if it were a local method.
4. It runs over **HTTP/2** (multiplexed streams) with **Protobuf** binary payloads.
5. Cross-cutting concerns (auth, logging, deadlines) live in **interceptors**, not handlers.

## Proto syntax essentials
```proto
syntax = "proto3";
package spike.v1;
option go_package = "…/spike/v1;spb";

message SendEmailPayload {
  string subject = 1;            // field NUMBER is what's on the wire — never reuse/change
  repeated string recipients = 3; // repeated = list
  EmailType type = 7;
}
enum EmailType { EMAIL_TYPE_UNSPECIFIED = 0; TRANSACTIONAL = 1; } // first value MUST be 0

service EmailModifierService {
  rpc SendEmail(SendEmailRequest) returns (SendEmailResponse);   // unary
}
```
Field-number rules: 1–536,870,911; 19,000–19,999 reserved; **numbers are the contract**, names aren't.

## The four RPC types
| Type | Proto | Go handler | Repo example |
|---|---|---|---|
| Unary | `F(Req) returns (Resp)` | `func(ctx, *Req) (*Resp, error)` | **all 3 services** |
| Server-stream | `F(Req) returns (stream Resp)` | `func(*Req, F_Server) error` | tom `Subscribe` |
| Client-stream | `F(stream Req) returns (Resp)` | `func(F_Server) error` | bob `UploadAsset` |
| Bidi-stream | `F(stream Req) returns (stream Resp)` | `func(F_Server) error` | **none in repo** |
Streaming handlers get a **stream object** (`.Send()`/`.Recv()`), not a plain req/resp.

## Status codes (the common ones)
| Code | Meaning | Our use |
|---|---|---|
| `OK` | success | — |
| `InvalidArgument` | bad input | validation failures |
| `NotFound` | entity missing | — |
| `AlreadyExists` | duplicate | `UniqueViolation` (via errorx elsewhere) |
| `PermissionDenied` | authn/authz failure | **all** auth failures (never Internal) |
| `ResourceExhausted` | limit/rate exceeded | size limits |
| `Unavailable` | transient; retry | triggers client retry |
| `DeadlineExceeded` | deadline passed | — |
| `Internal` | infra/unexpected | wrapped server errors |
Map at the **controller boundary** with `status.Error(codes.X, msg)`.

## Deadlines & metadata
- **Deadline** = absolute time, set by the *client*, propagated via `context`. Server honours it (our framework wraps each handler in `context.WithTimeout`, default 5s).
- **Metadata** = headers. In: `metadata.FromIncomingContext(ctx)`. Out: `metadata.NewOutgoingContext` / `AppendToOutgoingContext`. Our `token` header carries the JWT.

## Interceptors (our chain, in order)
```
ctxtags → grpc_zap (log) → payload-log → context-canceled remap
       → recovery (panic→Unknown) → timeout(5s)
       → auth (verify JWT + RBAC) → activity-log tracer
       → [handler]
```
Unary interceptor signature:
```go
func(ctx, req, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error)
// do work; enrich ctx; then: return handler(ctx, req)
```

## gRPC vs REST (interview table)
| | gRPC | REST |
|---|---|---|
| Contract | `.proto` (enforced) | OpenAPI (optional) |
| Payload | Protobuf (binary, small) | JSON (text) |
| Transport | HTTP/2 (multiplexed, streaming) | usually HTTP/1.1 |
| Streaming | native (4 types) | awkward (SSE/websockets) |
| Browser | needs grpc-web/gateway | native |
| Best for | internal service-to-service, low latency | public/browser APIs |

## Our repo — quick facts
```
Codegen       protoc via `make gen-proto-go` → proto/gen_go.sh   (NOT buf generate)
buf           lint & breaking checks only (make lint-proto)
Proto layout  proto/<service>/v1/*.proto  →  pkg/manabuf/<service>/v1  (cpb/npb/spb)
RPC types     our 3 services = UNARY only; streaming in tom/bob; no bidi anywhere
Server        implement bootstrap GRPCServicer[T]; grpc.NewServer via WithUnaryServerChain
Registration  SetupGRPC → spb.RegisterEmailModifierServiceServer(server, svc)
Auth          internal/usermgmt/pkg/interceptors (NOT golibs); rbacDecider per method
RBAC          every RPC in cmd/server/*/auth.go rbacDecider or server panics; nil = any authed
Internal RPCs ignoreAuthEndpoint + fakeJwtCtxEndpoint → synthetic school-admin claim
Errors        status.Error(codes.X) at controller; PermissionDenied for auth
Client dial   Resources.GRPCDial(name), insecure creds + B3; retry on Unavailable
Gateway       grpc-gateway for spike/notification at /<svc>/api/v1/proxy/* (fronts gandalf)
Health        grpc.health.v1 registered; reflection NOT registered
Unimplemented require_unimplemented_servers=false → embedding optional
```

## Interview one-liners
- *Why gRPC over REST?* Typed contract, small fast binary payloads, HTTP/2 multiplexing, native streaming — great for internal service-to-service.
- *Why Protocol Buffers?* A strict, versionable contract + compact binary encoding; the field *number* is the wire identity.
- *Why HTTP/2?* One connection, many concurrent streams, header compression → efficient + enables streaming.
- *Deadline vs timeout?* Deadline = absolute point in time set by the client and propagated; timeout = a duration you convert into one.
- *What's an interceptor?* Middleware wrapping every RPC for auth/logging/tracing/recovery — keeps handlers clean.
- *Unary vs streaming?* Unary = one-shot request/response; stream when the data is a sequence or open-ended (feed, upload, chat).
- *How does auth work here?* Interceptor reads the `token` metadata, verifies the JWT, injects claims into context, then checks the caller's roles against the per-method `rbacDecider`.
