agentsclimarketplace

Grpc architect

Skill ralvarezdev/ralvaskills/skills/protocols/grpc-architect

My personal, ever-growing collection of AI skills for OpenCode and Claude Code. Enforces strict clean architecture and professional standards.

Install
npx -y skills add ralvarezdev/ralvaskills --skill grpc-architect

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Vanilla gRPC standards — .proto services, status.Error with standard codes, domain→code mapping, interceptor chain (auth/log/recovery/validation/metrics), client deadlines, context propagation, reflection off in prod, bufconn testing. Language-agnostic; Go examples. Use when designing or reviewing a gRPC service.

SKILL.md

10.1 KB, as published. Nobody here has run it

gRPC Architecture

Vanilla gRPC over HTTP/2 — backend-to-backend services. Pair with protobuf-architect for schema design. Go-specific implementation skeletons in RECIPES.md; pinned deps in STACK.md. Other languages follow the same protocol-level conventions with idiomatic substitutions.

1. Service definition

One service per file, named after the resource. Methods are verb-noun, request and response are always typed messages — never raw primitives or google.protobuf.Empty as input. Example in RECIPES.md.

  • <Verb><Noun>Request / <Verb><Noun>Response naming for every method's I/O message. Even when the response is a single resource, prefer CreateUserResponse { User user = 1; } over returning User directly — leaves room to add fields without bumping the major version.
  • Pagination via cursor, mirroring rest-api-architect §5. ListUsersRequest { string cursor = 1; int32 limit = 2; }ListUsersResponse { repeated User users = 1; string next_cursor = 2; }.
  • google.protobuf.Empty only as a response type for "fire and forget" actions with no useful return. Never as input.

2. Error handling — status.Error with codes

Use gRPC standard codes, return errors via status.Error(code, msg). Map domain errors to codes in one central place (skeleton in RECIPES.md).

Standard codes — the ones architects actually use

CodeUse for
OKsuccess
INVALID_ARGUMENTrequest fails schema or business validation
FAILED_PRECONDITIONrequest valid but system state forbids it (e.g. delete a non-empty resource)
OUT_OF_RANGEnumeric / range-specific violation distinct from INVALID_ARGUMENT
UNAUTHENTICATEDmissing or invalid credentials
PERMISSION_DENIEDauthenticated but not authorized
NOT_FOUNDresource doesn't exist
ALREADY_EXISTSunique-constraint or idempotency-key collision (with a different body)
ABORTEDconcurrency conflict (ETag-equivalent) — client should re-fetch and retry
RESOURCE_EXHAUSTEDrate limit; per-tenant quota
DEADLINE_EXCEEDEDthe request didn't complete in time — set by the runtime
UNAVAILABLEtransient — load-balancer drained, restart in progress; client retries
INTERNALunexpected server-side failure — bug or external dependency error
UNIMPLEMENTEDmethod exists in proto but server doesn't handle it (use during rollout)

Don't reach for INTERNAL as a default. Map every known domain error to a specific code.

  • status.WithDetails attaches structured detail (google.rpc.ErrorInfo, google.rpc.BadRequest) when clients need machine-readable error context — equivalent to REST's RFC 7807 (see rest-api-architect §7). Always include a correlation id.
  • Never leak stack traces or DB errors to clients. Log server-side; return a generic INTERNAL with the correlation id.

3. Request / response shape

  • Always typed messages. Don't define a method as rpc Ping(StringValue) returns (StringValue) — wrap in PingRequest / PingResponse.
  • Validation at the boundary via protovalidate (see protobuf-architect §5). Enforced server-side via an interceptor (§4).
  • No business logic in generated handler files. Generated handlers are thin shims that call into service-layer code (same discipline as REST routers per fastapi-architect / gin-architect).

4. Interceptors — mandatory chain

Interceptors are gRPC's middleware. Order matters — outermost first: recovery → request-id → log → auth → validation → metrics. Full chain in RECIPES.md.

  • Recovery first — catches panics anywhere downstream and converts to INTERNAL with correlation id (never a stack trace).
  • Auth before validation — no point validating an unauthenticated request's body. Per-method authorization (scopes/roles) happens inside the handler or via a small WithAuthFunc interceptor.
  • Validation is centralized via protovalidate — don't hand-write validation in every handler. The interceptor calls validator.Validate(req) and returns INVALID_ARGUMENT with google.rpc.BadRequest details on failure.
  • Same interceptor chain for streaming RPCs via ChainStreamInterceptor. Streaming validation requires handling per-message in client/bidi streams.

5. Streaming patterns

gRPC supports four call types. Pick the simplest one that meets the requirement.

PatternUse forPitfalls
UnaryDefault — request/responseNone — start here
Server-streamServer emits N responses to one request (event feeds, paginated downloads that don't fit one response, log tail)Connection state outlives the request; resume tokens needed for restarts
Client-streamClient uploads N messages, server returns one summary (large uploads, batch ingest)Backpressure from server requires careful flow control
Bidi-streamGenuinely interactive (chat, collaborative editing, control protocols)Connection lifecycle complexity; reconnect / resume logic; deadlines
  • Don't reach for streaming "to save round-trips" — unary with proper pagination is usually fine and orders of magnitude simpler.
  • Server-streams need resume tokens. Pass start_after_id or a cursor in the request so a disconnected client can resume from a known point.
  • Bidi-streams need a clear protocol — define exactly which side sends what and when. Sketch the message flow in the .proto comments; future-you will thank you.
  • Set generous deadlines on streams — but always set them. An unbounded stream is a leak.

6. Deadlines & context propagation

Every gRPC call has a deadline. Clients set; servers respect; downstream calls inherit the remaining time. Client skeleton in RECIPES.md.

  • Server respects — check ctx.Err() periodically in long-running handlers; abort work the moment the deadline fires.
  • Propagate context to all downstream calls — DB queries, HTTP calls, other gRPC calls. Deadlines and cancellation flow automatically.
  • Default deadlines per call type: unary 5–30s; server-stream often much longer (minutes / hours) but always bounded.
  • Server-side deadline guard: wrap the entire handler in context.WithTimeout slightly less than the client deadline to leave headroom for response serialization.

7. Metadata vs message fields

Use metadata forUse message fields for
Auth tokens (authorization: Bearer ...)Business data
Request IDs / correlation IDs (read by middleware)Anything the handler reads as part of business logic
Tracing context (traceparent)
Rate-limit hints (x-tenant-id for routing)
  • Metadata is HTTP/2 headers under the hood. Don't send large payloads here.
  • Keys are case-insensitive ASCII; values are strings (binary metadata uses the -bin suffix).
  • Standardize one correlation-id header (e.g. x-request-id) — interceptor reads it on entry, injects into context, logs against it.

8. Reflection

Reflection enables grpcurl and IDE plugins to introspect the service without the .proto file — on in dev, off in production. It leaks the entire service surface. Skeleton in RECIPES.md. The health service (grpc.health.v1) is always on — load balancers and orchestrators need it.

9. Testing — bufconn for in-process

The google.golang.org/grpc/test/bufconn package gives an in-memory listener — full server + client without a real socket. Faster than net.Pipe, simpler than spinning up a test server on a port. Skeleton in RECIPES.md.

  • bufconn for unit + integration tests; spin a real server on a random port only when you specifically need the full network path (TLS, HTTP/2 frame behavior, etc.).
  • Table-driven tests per go-architect §9 — one row per (input, expected code, expected error type).
  • grpcurl is the manual-testing tool. Pin it in your task runner / mise config.

10. When to pick gRPC over REST

gRPC's wins are real but specific:

  • Backend-to-backend — gRPC's binary framing and HTTP/2 multiplexing beat JSON-over-HTTP/1.1 in throughput and tail latency at scale.
  • Strong contracts.proto is the canonical schema; clients in any language are generated. No OpenAPI drift.
  • Streaming — first-class server-stream / client-stream / bidi.
  • Compact wire format — binary; smaller than JSON for the same payload.

REST is the better default when:

  • Browser callers — vanilla gRPC isn't browser-callable without a gateway. Connect-RPC fixes this; consider it (or a separate REST facade) if browser is in scope.
  • Public APIs — external consumers expect REST; curl works without tooling; OpenAPI is the universal documentation format.
  • Cache-friendly reads — HTTP caching (ETag, Cache-Control) is built-in; gRPC has no equivalent.
  • Small scope — for one CRUD service, a Gin/FastAPI REST API is shorter to build.

The two coexist: gRPC for east-west backend traffic, REST for north-south client-facing endpoints. Connect-RPC lets one set of .proto files serve gRPC, gRPC-Web, and Connect (browser-friendly) — worth considering as the upgrade path. grpc-gateway is an alternative for REST/JSON facades but adds spec complexity and error-translation discipline.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.