Strongly Typed Hubs
Define typed hub interfaces to get compile-time safety and IntelliSense for client method calls.
Strongly Typed Hubs is a free C# Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Problem with String Method Names
Standard SignalR calls use SendAsync("MethodName", ...) with magic strings. A typo in the method name causes a silent failure — the client simply never receives the message. Strongly typed hubs solve this.
Defining the Client Interface
Create an interface that declares every method the server can invoke on the client. The method names and parameter types are your contract.
// INotificationClient.cs
public interface INotificationClient
{
Task ReceiveMessage(string user, string message);
Task OrderShipped(int orderId, string trackingNumber);
Task UserJoined(string username);
Task UserLeft(string username);
Task SystemAlert(string message);
}Creating a Strongly Typed Hub
Inherit from Hub<T> where T is your client interface. The Clients property is now typed — IntelliSense shows available methods, and typos are compile errors.
public class ChatHub : Hub<INotificationClient>
{
public async Task SendMessage(string message)
{
var user = Context.User!.Identity!.Name ?? "Anonymous";
// Compile-time safety — no magic strings!
await Clients.All.ReceiveMessage(user, message);
await Clients.Others.UserJoined(user); // IntelliSense works
}
}Targeting Specific Clients with Typed Hub
All standard targeting methods work on the typed hub: Clients.User, Clients.Group, Clients.Caller, Clients.Client — all returning the typed interface.
public async Task NotifyOrderShipped(int orderId, string customerId)
{
await Clients.User(customerId)
.OrderShipped(orderId, "TRK-12345");
await Clients.Group("admins")
.SystemAlert($"Order {orderId} shipped to {customerId}");
await Clients.Caller.ReceiveMessage("System", "Notification sent");
}Typed IHubContext
Inject IHubContext<THub, TClient> into services to get the same compile-time safety when pushing messages from outside the hub.
public class ShipmentService
{
private readonly IHubContext<ChatHub, INotificationClient> _hub;
public ShipmentService(
IHubContext<ChatHub, INotificationClient> hub) => _hub = hub;
public async Task ProcessShipmentAsync(int orderId, string customerId)
{
// Fully typed — no magic strings
await _hub.Clients.User(customerId)
.OrderShipped(orderId, "TRK-99999");
}
}Client Interface Rules
Methods in the client interface must return Task (not Task<T>). SignalR fires-and-forgets client invocations — there is no return value from the client to the server.
// CORRECT
public interface IMyClient
{
Task ReceiveMessage(string msg); // OK
Task UpdateProgress(int percent); // OK
Task<string> GetUserInput(); // NOT SUPPORTED
}
// Return types other than Task are not supported in hub client interfaces
// For request-response patterns, use client-callable hub methods insteadCombining Typed Hubs with Groups
Typed groups work identically to untyped — the API is the same, just with compile-time method names.
public class RoomHub : Hub<IRoomClient>
{
public async Task JoinRoom(string room)
{
await Groups.AddToGroupAsync(Context.ConnectionId, room);
await Clients.Group(room).UserJoined(Context.User!.Identity!.Name!);
}
public async Task BroadcastToRoom(string room, string message)
{
var user = Context.User!.Identity!.Name!;
await Clients.Group(room).ReceiveMessage(user, message);
}
}Testing Strongly Typed Hubs
Since client method calls go through an interface, you can mock the client in unit tests to verify that the correct methods were called with the right arguments.
// NSubstitute test
var mockClients = Substitute.For<IHubCallerClients<INotificationClient>>();
var mockAllClients = Substitute.For<INotificationClient>();
mockClients.All.Returns(mockAllClients);
var hub = new ChatHub { Clients = mockClients };
await hub.SendMessage("Hello!");
await mockAllClients.Received(1).ReceiveMessage("System", "Hello!");Multiple Hubs per Application
An application can have multiple hub endpoints for different real-time features, each with its own typed client interface.
// Three separate hubs
app.MapHub<ChatHub>("/hubs/chat");
app.MapHub<NotificationHub>("/hubs/notifications");
app.MapHub<DashboardHub>("/hubs/dashboard");
// Each with its own client interface:
// Hub<IChatClient>, Hub<INotificationClient>, Hub<IDashboardClient>Sending Structured Data
Client interface methods can accept complex DTOs. SignalR serializes them to JSON automatically — ensure your types are serializable.
public interface IOrderClient
{
Task OrderUpdated(OrderStatusDto status);
}
public record OrderStatusDto(
int OrderId,
string Status,
decimal Total,
DateTime UpdatedAt);
// Usage in hub:
await Clients.User(customerId).OrderUpdated(
new OrderStatusDto(order.Id, order.Status.ToString(),
order.Total, DateTime.UtcNow));Quick Check
What is the return type restriction for methods in a SignalR hub client interface?
Recap: Strongly Typed Hubs
Key takeaways:
- Define an interface (IMyClient) listing all methods the server can call on clients
- Inherit from
Hub<IMyClient>— Clients property becomes fully typed - Inject
IHubContext<THub, TClient>for typed pushes from outside the hub - Client interface methods must return
Task, notTask<T> - Interface-based clients are mockable — strongly typed hubs are easier to test
Frequently asked questions
Is the “Strongly Typed Hubs” lesson free?
Yes — the full text of “Strongly Typed Hubs” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.
What will I learn in “Strongly Typed Hubs”?
Define typed hub interfaces to get compile-time safety and IntelliSense for client method calls. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Strongly Typed Hubs” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.