
The Wolverine 5.0 release earlier last last week (finally) added a long requested SignalR transport.
The SignalR library from Microsoft isn’t hard to use from Wolverine for simplistic WebSockets or Server Sent Events usage as it was, but what if you want a server side application to exchange any number of different messages between a browser (or other WebSocket client because that’s actually possible) and your server side code in a systematic way? To that end, Wolverine now supports a first class messaging transport for SignalR. To get started, just add a Nuget reference to the WolverineFx.SignalR library:
dotnet add package WolverineFx.SignalR
There’s a very small sample application called WolverineChat in the Wolverine codebase that just adapts Microsoft’s own little sample application to show you how to use Wolverine.SignalR from end to end in a tiny ASP.Net Core + Razor + Wolverine application. The server side bootstrapping is at minimum, this section from the Wolverine bootstrapping within your Program file:
builder.UseWolverine(opts =>
{
// This is the only single line of code necessary
// to wire SignalR services into Wolverine itself
// This does also call IServiceCollection.AddSignalR()
// to register DI services for SignalR as well
opts.UseSignalR(o =>
{
// Optionally configure the SignalR HubOptions
// for the WolverineHub
o.ClientTimeoutInterval = 10.Seconds();
});
// Using explicit routing to send specific
// messages to SignalR. This isn't required
opts.Publish(x =>
{
// WolverineChatWebSocketMessage is a marker interface
// for messages within this sample application that
// is simply a convenience for message routing
x.MessagesImplementing<WolverineChatWebSocketMessage>();
x.ToSignalR();
});
});
And a little bit down below where you configure your ASP.Net Core execution pipeline:
// This line puts the SignalR hub for Wolverine at the
// designated route for your clients
app.MapWolverineSignalRHub("/api/messages");
On the client side, here’s a crude usage of the SignalR messaging support in raw JavaScript:
// Receiving messages from the server
connection.on("ReceiveMessage", function (json) {
// Note that you will need to deserialize the raw JSON
// string
const message = JSON.parse(json);
// The client code will need to effectively do a logical
// switch on the message.type. The "real" message is
// the data element
if (message.type == 'ping'){
console.log("Got ping " + message.data.number);
}
else{
const li = document.createElement("li");
document.getElementById("messagesList").appendChild(li);
li.textContent = `${message.data.user} says ${message.data.text}`;
}
});
and this code to send a message to the server:
document.getElementById("sendButton").addEventListener("click", function (event) {
const user = document.getElementById("userInput").value;
const text = document.getElementById("messageInput").value;
// Remember that we need to wrap the raw message in this slim
// CloudEvents wrapper
const message = {type: 'chat_message', data: {'text': text, 'user': user}};
// The WolverineHub method to call is ReceiveMessage with a single argument
// for the raw JSON
connection.invoke("ReceiveMessage", JSON.stringify(message)).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
I should note here that we’re utilizing Wolverine’s new CloudEvents support for the SignalR messaging to Wolverine, but in this case the only single elements that are required are data and type. So if you had a message like this:
public record ChatMessage(string User, string Text) : WolverineChatWebSocketMessage;
Your JSON envelope that is sent from the server to the client through the new SignalR transport would be like this:
For web socket message types that are marked with the new WebSocketMessage interface, Wolverine is using kebab casing of the type name for Wolverine’s own message type name alias under the theory that that naming style is more or less common in JavaScript world.
I should also say that a first class SignalR messaging transport for Wolverine has been frequently requested over the years, but I didn’t feel confident building anything until we had more concrete use cases with CritterWatch. Speaking of that…
How we’re using this in CritterWatch
The very first question we got about this feature was more or less “why would I care about this?” To answer that, let me talk just a little bit about the ongoing development with JasperFx Software’s forthcoming “CritterWatch” tool:

CritterWatch is going to involve a lot of asynchronous messaging and processing between the web browser client, the CritterWatch web server application, and the CritterStack (Wolverine and/or Marten in this case) systems that CritterWatch is monitoring and administrating. The major point here is that we need to issue a about three dozen different command messages from the browser to CritterWatch that will kick off long running asynchronous processes that will trigger workflows in other CritterStack systems that will eventually lead to CritterWatch sending messages all the way back to the web browser clients.
The new SignalR transport also provides mechanisms to get the eventual responses back to the original Web Socket connection that triggered the workflow and several mechanisms for working with SignalR connection groups as well.
Using web sockets gives us one single mechanism to issue commands from the client to the CritterWatch service, where the command messages are handled as you’d expect by Wolverine message handlers with all the prerequisite middleware, tracing, and error handling you normally get from Wolverine as well as quick access to any service in your server’s IoC container. Likewise, we can “just” publish from our server to the client through cascading messages or IMessageBus.PublishAsync() without any regard for whether or not that message is being routed through SignalR or any other message transport that Wolverine supports.
Web Socket Publishing from Asynchronous Marten Projection Updates
It’s been relatively common in the past year for me to talk through the utilization of SignalR and Web Sockets (or Server Side Events) to broadcast updates from asynchronously running Marten projections.
Let’s say that you have an application using event sourcing with Marten and you use the Wolverine integration with Marten like this bit from the CritterWatch codebase:
opts.Services.AddMarten(m =>
{
// Other stuff..
m.Projections.Add<CritterServiceProjection>(ProjectionLifecycle.Async);
})
// This is the key part, just calling IntegrateWithWolverine() adds quite a few
// things to Marten including the ability to use Wolverine messaging from within
// Marten RaiseSideEffects() methods
.IntegrateWithWolverine(w =>
{
w.UseWolverineManagedEventSubscriptionDistribution = true;
});
We have this little message to communicate to the client when configuration changes are detected on the server side:
// The marker interface is just a helper for message routing
public record CritterServiceUpdated(CritterService Service) : ICritterStackWebSocketMessage;
And this little bit of routing in Wolverine:
opts.Publish(x =>
{
x.MessagesImplementing<ICritterStackWebSocketMessage>();
x.ToSignalR();
});
And we have a single stream projection in CritterWatch like this:
public class CritterServiceProjection
: SingleStreamProjection<CritterService, string>
And finally, we can use the RaiseSideEffects() hood that exists in the Marten SingleStream/MultiStreamProjection to run some code every time an aggregated projection is updated:
public override ValueTask RaiseSideEffects(IDocumentOperations operations, IEventSlice<CritterService> slice)
{
// This is the latest version of CritterService
var latest = slice.Snapshot;
// CritterServiceUpdated will be routed to SignalR,
// so this is de facto updating all connected browser
// clients at runtime
slice.PublishMessage(new CritterServiceUpdated(latest!));
return ValueTask.CompletedTask;
}
And after admittedly a little bit of wiring, we’re at a point where we can happily send messages from asynchronous Marten projections through to Wolverine and on to SignalR (or any other Wolverine messaging mechanism too of course) in a reliable way.
Summary
I don’t think that this new transport is necessary for simpler usages of SignalR, but could be hugely advantageous for systems where there’s a multitude of logical messaging back and forth from the web browser clients to the backend.

Introducing vibe coding in Google AI Studio