Skip to content

The search box in the website knows all the secrets—try it!

For any queries, join our Discord Channel to reach us faster.

JasperFx Logo

JasperFx provides formal support for Wolverine and other JasperFx libraries. Please check our Support Plans for more details.

gRPC Services with Wolverine

INFO

The WolverineFx.Grpc package lets you expose Wolverine handlers as ASP.NET Core gRPC services with minimal wiring. It supports both the code-first (protobuf-net.Grpc) and proto-first (Grpc.Tools) styles.

Why gRPC?

If you're already using Wolverine to orchestrate message handlers, sagas, and HTTP endpoints, gRPC gives you another edge protocol for the same handlers. Benefits:

  • Strongly-typed contracts shared across .NET and non-.NET services via .proto files, or code-first contracts that never leave C#.
  • Streaming first-class — plays naturally with Wolverine's IMessageBus.StreamAsync<T>.
  • Wolverine handler reuse — the same handler can back a REST endpoint, an async message, and a gRPC call without duplication.
  • Canonical error semantics — ordinary .NET exceptions thrown by a handler are mapped to the right gRPC StatusCode automatically, following Google AIP-193.

What's in this section

Start here to get Wolverine's gRPC adapter running, then drill into the page that matches what you're building:

  • How gRPC Handlers Work — the service → IMessageBus → handler flow, how it differs from HTTP and messaging handlers, and how OpenTelemetry traces survive the hop.
  • Code-First and Proto-First Contracts — the two contract styles side by side so you can pick (or mix) them.
  • Error Handling — the default AIP-193 exception → StatusCode table plus the opt-in google.rpc.Status pipeline for rich, structured details.
  • Streaming — server streaming today, bidirectional via a manual bridge, and the shape of the cancellation contract.
  • Typed gRPC ClientsAddWolverineGrpcClient<T>(), the Wolverine-flavored wrapper over Grpc.Net.ClientFactory that adds envelope-header propagation and RpcException → typed-exception translation on the consuming side.
  • Samples — runnable end-to-end samples with pointers to the equivalent official grpc-dotnet examples for comparison.

Getting Started

Add the integration package and register it alongside the usual Wolverine bootstrap. AddWolverineGrpc does not register a gRPC host — callers decide whether they want code-first or proto-first (or both):

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Host.UseWolverine(opts =>
{
    opts.ApplicationAssembly = typeof(Program).Assembly;
});

// Pick one (or both):
builder.Services.AddCodeFirstGrpc();   // protobuf-net.Grpc — code-first
builder.Services.AddGrpc();            // Grpc.AspNetCore — proto-first

// Wolverine's gRPC adapter (exception interceptor, discovery, codegen pipeline)
builder.Services.AddWolverineGrpc();

var app = builder.Build();
app.UseRouting();

// Discovers every '*GrpcService' / [WolverineGrpcService] type and maps it for you.
// Proto-first stubs generate a concrete wrapper on the fly.
app.MapWolverineGrpcServices();

app.Run();

From here, How gRPC Handlers Work walks through what MapWolverineGrpcServices actually wires up and why a gRPC handler is just an ordinary Wolverine handler with a thin service shim on top.

Runnable Samples

Six end-to-end samples live under src/Samples/. Five are the classic trio shape (server, client, shared messages); OrderChainWithGrpc is a quartet because its proof-point is a chain between two Wolverine servers. dotnet run them side by side. See Samples for full walkthroughs and comparisons to the official grpc-dotnet examples.

SampleShapeWhat to copy
PingPongWithGrpcCode-first unary[ServiceContract] + WolverineGrpcServiceBase forwarding to a plain handler
PingPongWithGrpcStreamingCode-first server streamingHandler returning IAsyncEnumerable<T>, forwarded via Bus.StreamAsync<T>
GreeterProtoFirstGrpcProto-first unary + server streaming + exception mappingAbstract [WolverineGrpcService] stub subclassing a generated *Base + handlers
RacerWithGrpcCode-first bidirectional streamingPer-update bridge: client IAsyncEnumerable<TReq>Bus.StreamAsync<TResp> for each item
GreeterWithGrpcErrorsCode-first rich error detailsFluentValidation → BadRequest plus inline MapExceptionPreconditionFailure, with a client that unpacks both
OrderChainWithGrpcWolverine → Wolverine chain via AddWolverineGrpcClient<T>()Typed client injected into a handler; envelope propagation + typed-exception round-trip with zero user plumbing

API Reference

Type / MemberPurpose
AddWolverineGrpc()Registers the interceptor, proto-first discovery graph, and codegen pipeline.
MapWolverineGrpcServices()Discovers and maps all gRPC services (code-first and proto-first).
WolverineGrpcServiceBaseOptional base class exposing an IMessageBus property Bus.
[WolverineGrpcService]Opt-in marker for classes that don't match the GrpcService suffix.
WolverineGrpcExceptionMapper.Map(ex)The public mapping table — use directly in custom interceptors.
WolverineGrpcExceptionInterceptorThe registered gRPC interceptor; exposed for diagnostics.
opts.UseGrpcRichErrorDetails(...)Opt-in google.rpc.Status pipeline — see Error Handling.
opts.UseFluentValidationGrpcErrorDetails()Bridge: ValidationExceptionBadRequest (from WolverineFx.FluentValidation.Grpc).
IGrpcStatusDetailsProviderCustom provider seam for building google.rpc.Status from an exception.
IValidationFailureAdapterPlug-in point for translating library-specific validation exceptions into BadRequest.FieldViolations.
AddWolverineGrpcClient<T>()Registers a Wolverine-flavored typed gRPC client — see Typed gRPC Clients.
WolverineGrpcClientOptionsNamed options for a typed client: Address, PropagateEnvelopeHeaders, MapRpcException.
WolverineGrpcExceptionMapper.MapToException(rpc)Inverse of Map — client-side RpcException → typed .NET exception table.

Current Limitations

  • Client streaming and bidirectional streaming have no out-of-the-box adapter path yet — there is no IMessageBus.StreamAsync<TRequest, TResponse> overload, and proto-first stubs with these method shapes fail fast at startup with a clear error rather than silently skipping. In code-first you can still implement bidi manually in the service by bridging each incoming item through Bus.StreamAsync<TResp>(item, ct) — see Streaming for the pattern and the RacerWithGrpc sample.
  • Exception mapping of the canonical Exception → StatusCode table is not yet user-configurable on the server side (follow-up item). Rich, structured responses are already available — see Error Handling. On the client side, WolverineGrpcClientOptions.MapRpcException already allows per-client overrides — see Typed gRPC Clients.
  • MiddlewareScoping.Grpc middleware — the enum value ships and is honored by Wolverine's discovery primitives, but no code path yet weaves [WolverineBefore(MiddlewareScoping.Grpc)] / [WolverineAfter(MiddlewareScoping.Grpc)] methods into the generated gRPC service wrappers. The attribute is safe to apply — it compiles, it is correctly filtered away from message-handler and HTTP chains, and it will start firing once the codegen path (tracked as M15) lands — but today nothing runs at RPC time. Until then, middleware that needs to execute on gRPC calls should live in a custom gRPC interceptor rather than rely on the attribute or on services.AddWolverineGrpc(g => g.AddMiddleware<T>()) (both take effect together in M15).

Roadmap

The gRPC integration has a handful of deferred items that are known-good fits but haven't shipped yet. They're listed here so contributors can plan around them and consumers know what's coming.

  • MiddlewareScoping.Grpc codegen weaving (M15) — attribute-based middleware on gRPC stubs (see Current Limitations above). Phase 0 landed the discovery + options surface; Phase 1 will wire execution into the generated GrpcServiceChain wrappers.
  • Validate convention → Status? — HTTP handlers already support an opt-in Validate method whose non-null return short-circuits the call. The gRPC equivalent would return Grpc.Core.Status? (or a richer google.rpc.Status) so a handler could express "this call is invalid, return InvalidArgument with these field violations" without throwing. Deferred because it lands cleanest on top of the code-first codegen work below.
  • Code-first codegen parity — proto-first services flow through a generated GrpcServiceChain with the usual JasperFx codegen pipeline; code-first services (the WolverineGrpcServiceBase path) currently resolve dependencies via service location inside each method. Generating per-method code files for code-first services — matching the HTTP and message handler story — is the prerequisite for the Validate convention above and for tighter Lamar/MSDI optimization.
  • Hybrid handler shape (HTTP + gRPC + messaging on one type) — open design question. The hybrid HTTP/message handler pattern works today for two protocols; extending it to three raises naming and scoping questions (MiddlewareScoping only permits one value per [Middleware] attribute, and the handler method name conventions overlap). No concrete plan yet — feedback welcome on the tracking issue.

Released under the MIT License.