Quick Start
Get started with Mocha in under five minutes. Install packages, register the message bus, define an event handler, publish your first event, and verify it works.
By the end of this guide, you will have an ASP.NET Core app that publishes an OrderPlaced event and handles it - all running in-process with the InMemory transport.
Here is what you are building:
Create the project
dotnet new web -n MochaQuickStartcd MochaQuickStart
Install the packages
You need two packages: the core bus and the InMemory transport.
dotnet add package Mochadotnet add package Mocha.Transport.InMemory
The InMemory transport keeps everything in-process - no broker to install, no infrastructure to configure. It is the fastest way to get started.
Define a message
A message is a plain C# record. Create a file called OrderPlaced.cs:
// OrderPlaced.csnamespace MochaQuickStart;
public sealed record OrderPlaced( Guid OrderId, string ProductName, decimal Amount);
No base class, no marker interface. Any record or class works as a message.
Create a handler
A handler is a class that implements IEventHandler<T>. Create a file called OrderPlacedHandler.cs:
using Mocha;
namespace MochaQuickStart;
public class OrderPlacedHandler(ILogger<OrderPlacedHandler> logger) : IEventHandler<OrderPlaced>{ public ValueTask HandleAsync( OrderPlaced message, CancellationToken cancellationToken) { logger.LogInformation( "Order received: {OrderId} - {ProductName} for {Amount:C}", message.OrderId, message.ProductName, message.Amount);
return ValueTask.CompletedTask; }}
The bus calls HandleAsync every time an OrderPlaced event is published. The handler receives the deserialized message and a cancellation token.
Register the bus
Open Program.cs and replace its contents:
// Program.csusing Mocha;using Mocha.Transport.InMemory;using MochaQuickStart;
var builder = WebApplication.CreateBuilder(args);
// Register the message bus, handlers, and transportbuilder.Services .AddMessageBus() .AddMochaQuickStart() // source-generated - discovers all handlers in this assembly .AddInMemory();
var app = builder.Build();
// Endpoint that publishes an eventapp.MapPost("/orders", async (IMessageBus bus) =>{ var orderPlaced = new OrderPlaced( OrderId: Guid.NewGuid(), ProductName: "Mechanical Keyboard", Amount: 149.99m);
await bus.PublishAsync(orderPlaced, CancellationToken.None);
return Results.Ok(new { orderPlaced.OrderId, Status = "Published" });});
app.Run();
Each registration line has a single responsibility:
AddMessageBus()- registers the bus runtime and core services into DI.AddMochaQuickStart()- source-generated method that discovers and registers all handlers in this assembly. Named after the project - to customize the name, see Handler Registration.AddInMemory()- adds the InMemory transport; messages stay in-process.
Publish and verify
Run the app:
dotnet run
Check your console output for the actual URL. ASP.NET Core's default port may differ depending on your SDK version and launch settings. Then, in another terminal, send a POST request using that URL:
curl -X POST http://localhost:5000/orders
You should see JSON from curl:
{ "orderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "Published" }
And in the application console, the handler's log message appears:
info: MochaQuickStart.OrderPlacedHandler[0] Order received: a1b2c3d4-e5f6-7890-abcd-ef1234567890 - Mechanical Keyboard for $149.99
If you see that log line, it worked.
What happened?
Your POST request hit the /orders endpoint, which called PublishAsync on IMessageBus. The bus serialized the OrderPlaced record and handed it to the InMemory transport. The transport delivered it to the registered receive endpoint, which ran the message through the pipeline and invoked HandleAsync on your OrderPlacedHandler. The log line you see is proof the full path executed: publisher to bus to transport to handler.
Next steps
You have a working message bus. Here is where to go next:
- Understand messages: Messages - learn what a message is, how the envelope wraps it, and naming conventions for events and commands.
- Learn the three patterns: Messaging Patterns - understand when to use pub/sub events, commands, and request/reply.
- Move to production: Transports - switch from InMemory to RabbitMQ for real workloads.
Now that you have a working app, learn how messages work in Messages.
Runnable example: Examples/QuickStart
Full demo: The Demo application shows a complete e-commerce system with Catalog, Billing, and Shipping services communicating through Mocha and orchestrated with .NET Aspire.