name: grpc-standards description: "Use when working with gRPC services, protobuf definitions, Protocol Buffers, streaming RPCs, interceptors, or gRPC client configuration in .NET. Provides domain-specific rules layered on top of base architectural standards."
Skill: gRPC Service Standards
Identity
| Field | Value |
|---|---|
| Name | gRPC Service Standards |
| Domain | API, Messaging, RPC |
| Level | Feature |
| Tags | grpc, protobuf, streaming, interceptors, rpc |
When to Apply
Activate this skill when the task involves:
- Defining or editing
.protofiles - Implementing gRPC service classes
- Server or client streaming patterns
- gRPC interceptors (logging, auth, error mapping)
- Deadline and timeout configuration
- gRPC client registration and channel management
Rules
These rules layer on top of base architectural standards. On conflict, these win.
<!-- SHARED:rules/grpc.md -->gRPC — Service Standards
Apply these rules in addition to _base.md for projects using gRPC services.
Protobuf Conventions
- One
.protofile per service — file name matches service name:order_service.proto. - Use
snake_casefor field names,PascalCasefor message and service names (protobuf convention). - Set
option csharp_namespaceexplicitly to control generated C# namespace. - Version APIs via package name:
package myapp.orders.v1;— never reuse package names for breaking changes. - Every RPC method has a dedicated
RequestandResponsemessage — never reuse messages across RPCs. - Use
google.protobuf.Timestampfor dates,google.protobuf.Durationfor time spans,google.protobuf.StringValuefor nullable strings.
Service Implementation
- Inherit from generated
Baseclass:public class OrderService : Orders.OrdersBase. - Keep service methods thin — delegate to Application layer handlers (same as Minimal API handlers).
- Inject dependencies via constructor DI — gRPC services are registered as scoped by default.
- Always accept
ServerCallContextand propagate itsCancellationTokento downstream calls. - Return domain errors via
RpcExceptionwith appropriateStatusCode— never throw raw exceptions.
Streaming Patterns
- Server streaming: use
async+yield returnpattern or write toIServerStreamWriter<T>in a loop. - Client streaming: read from
IAsyncStreamReader<T>untilMoveNext()returnsfalse. - Bidirectional: use
Task.WhenAllto read and write concurrently, not sequentially. - Always check
context.CancellationTokenin streaming loops — abort cleanly on client disconnect. - Set reasonable
MaxReceiveMessageSizeandMaxSendMessageSize(default 4MB is often too small for file transfers).
Deadlines & Timeouts
- Every client call must set a deadline:
client.GetOrderAsync(req, deadline: DateTime.UtcNow.AddSeconds(5)). - Propagate incoming deadlines to downstream calls — use
context.Deadlinefor cascading timeouts. - Handle
StatusCode.DeadlineExceededexplicitly in client error handling. - Set global channel options for default deadline:
GrpcChannelOptions.MaxReceiveMessageSize.
Error Handling
- Map domain errors to gRPC status codes:
| Domain Error | gRPC StatusCode | When |
|---|---|---|
| Not found | NotFound | Resource doesn't exist |
| Validation failure | InvalidArgument | Bad request data |
| Already exists | AlreadyExists | Duplicate create |
| Permission denied | PermissionDenied | Auth check failed |
| Unauthorized | Unauthenticated | No/invalid credentials |
| Business rule violation | FailedPrecondition | State doesn't allow operation |
| Timeout | DeadlineExceeded | Operation took too long |
| Internal error | Internal | Unexpected server error |
- Include detail metadata in
RpcExceptionfor debugging — never expose stack traces. - Use interceptors for global error mapping — don't repeat
try/catchin every RPC method.
Interceptors
- Use server interceptors for cross-cutting concerns: logging, metrics, auth, error mapping.
- One concern per interceptor — same single-responsibility as middleware.
- Register interceptors in
AddGrpc(options => options.Interceptors.Add<LoggingInterceptor>()). - Client interceptors for: retry, deadline propagation, auth token injection.
Client Configuration
- Use
GrpcChannel.ForAddresswithIHttpClientFactoryfor connection management. - Register typed clients via
AddGrpcClient<OrderService.OrderServiceClient>()in DI. - Enable retry via
ServiceConfig— gRPC has built-in retry policy support. - Use
CallInvokerfor testing — mock at the channel level, not the service level.
Common Anti-Patterns
| Anti-Pattern | Fix |
|---|---|
| No deadline on client calls | Always set deadline: parameter |
Throwing Exception from service method | Return RpcException(new Status(StatusCode.X, "msg")) |
| Large messages without streaming | Use server streaming for collections > 100 items |
| Blocking in streaming loop | Use async/await with CancellationToken |
| Reusing request/response messages across RPCs | One dedicated message pair per RPC method |
new GrpcChannel() per call | Use DI with AddGrpcClient<T>() |
Missing option csharp_namespace in .proto | Generates messy namespace — always specify |
| Sequential read/write in bidirectional stream | Use Task.WhenAll for concurrent read and write |
See Also
api-design.md— RESTful conventions, HTTP methods, pagination, error responsesresilience.md— Polly v8 resilience pipelines, retry, circuit breaker, timeoutauth.md— Authentication, authorization, OAuth/OIDC, secrets managementresult-error-handling.md— Result object pattern, error constants, exception boundaries