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, gRPC-scoped middleware and structural policies, 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

Eight end-to-end samples live under src/Samples/. Most 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>
GreeterCodeFirstGrpcCode-first generated implementation[WolverineGrpcService] on an interface — Wolverine generates the service class; no concrete class written
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
ProgressTrackerWithGrpcCode-first server streaming + cancellationRealistic job-progress stream: handler yields JobProgress updates; client cancels mid-stream
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]On an interface: Wolverine generates the concrete service class, reducing hand-written boilerplate. On a class: opt-in marker for concrete code-first services and abstract proto-first stubs that don't match the GrpcService suffix.
opts.AddMiddleware<T>(filter?)Register a middleware type applied to all Wolverine-managed gRPC chains (proto-first, code-first generated, and hand-written) at codegen time. Optional Func<IChain, bool> narrows which chains receive it — pattern-match on GrpcServiceChain, CodeFirstGrpcServiceChain, or HandWrittenGrpcServiceChain for kind-specific targeting. See Middleware and Policies.
opts.AddPolicy<T>() / AddPolicy(policy)Register an IGrpcChainPolicy for structural chain customization. Receives all three chain kinds as typed lists. See Middleware and Policies.
IGrpcChainPolicyInterface for structural gRPC chain policies. Apply(protoFirst, codeFirst, handWritten, rules, container) — typed access to all chain kinds, no casting required.
ModifyGrpcServiceChainAttributeAbstract attribute for per-chain customization of proto-first chains. Apply to the stub class.
ModifyHandWrittenGrpcServiceChainAttributeAbstract attribute for per-chain customization of hand-written code-first service chains. Apply to the service class.
ModifyCodeFirstGrpcServiceChainAttributeAbstract attribute for per-chain customization of generated code-first service chains. Apply to the [ServiceContract] interface.
WolverineGrpcExceptionMapper.Map(ex)The public mapping table — use directly in custom interceptors.
WolverineGrpcExceptionInterceptorThe registered gRPC interceptor; exposed for diagnostics.
opts.MapException<T>(StatusCode)Override the server-side Exception → StatusCode mapping for a specific type — see Error Handling.
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

  • Pure client streaming (stream TRequest → TResponse) has no out-of-the-box adapter path yet. Proto-first stubs that declare this shape fail fast at startup with a clear error rather than silently skipping. Bidirectional streaming is fully supported — see Streaming.

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.

  • 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.