# InMemory Transport - Mocha

> Set up the InMemory transport for development, testing, and single-process messaging scenarios in Mocha.

Canonical source: https://chillicream.com/docs/mocha/transports/in-memory

The InMemory transport routes messages through in-process topics and queues without any external broker. Messages never leave the application process and are never persisted to disk.

## Install and register

**1.** Install the package:

Bash

```
dotnet add package Mocha.Transport.InMemory
```

**2.** Register the transport:

C#

```
using Mocha;
using Mocha.Transport.InMemory;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddMessageBus()
    .AddEventHandler<OrderPlacedEventHandler>()
    .AddInMemory(); // One line - no configuration needed

var app = builder.Build();
app.Run();
```

`.AddInMemory()` registers the transport with default conventions. Topics, queues, and bindings are created automatically based on your registered handlers and message types.

## Verify it works

Add an endpoint that publishes through the bus and check that your handler receives it:

C#

```
app.MapPost("/orders", async (IMessageBus bus) =>
{
    await bus.PublishAsync(new OrderPlacedEvent
    {
        OrderId = Guid.NewGuid(),
        CustomerId = "customer-1",
        TotalAmount = 99.99m
    }, CancellationToken.None);

    return Results.Ok();
});
```

Send a POST request to `/orders`. Because the InMemory transport dispatches within the same process, delivery is near-instantaneous and your handler's logic executes before the HTTP response returns.

## How the in-process topology works

The InMemory transport replicates the same topic/queue/binding model that RabbitMQ uses - except everything lives inside your process memory. There is no broker, no network, and no serialization to disk. When you call `PublishAsync`, Mocha routes the message through an in-process topic to every queue bound to that topic, then invokes the handler bound to each queue.

This model is why swapping `.AddInMemory()` for `.AddRabbitMQ()` requires no application code changes. The same topic/queue/binding topology that runs in-process with InMemory is provisioned on the broker when you switch to RabbitMQ.

By default, the InMemory transport processes messages sequentially in the order they are published. Each message is delivered to its handler before the next one is dispatched.

Note

InMemory tests exercise handler logic and message routing, but not RabbitMQ-specific behavior such as topology conflicts, acknowledgement semantics, connection recovery, or quorum queue characteristics. For testing broker-specific behavior, use a real broker in a test container.

The following shows the default topology Mocha creates when you register an event handler with the InMemory transport:

## Configure queues

Use `transport.Queue("name")` when you want to choose the queue name, bind multiple handlers to one queue, or configure receive settings. The queue builder is the easiest way to customize in-memory topology because it combines queue declaration, handler binding, convention binding, and endpoint settings in one place.

C#

```
builder.Services
    .AddMessageBus()
    .AddEventHandler<OrderPlacedEventHandler>()
    .AddInMemory(transport =>
    {
        transport.BindExplicitly();

        transport.Queue("order-processing")
            .BindImplicitly()
            .MaxConcurrency(5)
            .FaultEndpoint("order-errors")
            .Handler<OrderPlacedEventHandler>();
    });
```

`BindExplicitly()` at the transport scope means only queues you configure are used for receiving. `BindImplicitly()` on the queue tells Mocha to keep the convention-derived topic binding for the messages handled by that queue.

Calling `Queue("name")` without `Handler<T>()`, `Consumer<T>()`, or `Receives<T>()` declares only the in-memory queue. Add a handler, consumer, or received message type when the queue should also consume messages.

C#

```
transport.Queue("audit")
    .Receives<OrderPlacedEvent>();
```

## Declare topology resources

The InMemory transport auto-generates topology from your handler registrations and queue builders.

Caution

Use `DeclareTopic()`, `DeclareQueue()`, and `DeclareBinding()` only when you need topology resources that are not represented by a receiving queue builder. For handler queues, prefer `transport.Queue("name")`.

To declare infrastructure-only topology:

C#

```
builder.Services
    .AddMessageBus()
    .AddInMemory(transport =>
    {
        // Declare a topic
        transport.DeclareTopic("order-events");

        // Declare a queue
        transport.DeclareQueue("billing-orders");

        // Bind the topic to the queue
        transport.DeclareBinding("order-events", "billing-orders");
    });
```

## Configure convention endpoints

Use `transport.Handler<T>()` at the end of the transport configuration when you want to keep the convention-derived queue name and only tune one handler endpoint:

C#

```
builder.Services
    .AddMessageBus()
    .AddEventHandler<OrderPlacedEventHandler>()
    .AddInMemory(transport =>
    {
        transport.Handler<OrderPlacedEventHandler>()
            .ConfigureEndpoint(e => e.MaxConcurrency(5));
    });
```

The handler keeps its convention-derived endpoint name. `ConfigureEndpoint()` can be called multiple times - actions compose in declaration order:

C#

```
transport.Handler<OrderPlacedEventHandler>()
    .ConfigureEndpoint(e => e.MaxConcurrency(5))
    .ConfigureEndpoint(e => e.FaultEndpoint("order-errors"));
```

For raw `IConsumer` types, use `transport.Consumer<T>()`:

C#

```
transport.Consumer<OrderAuditConsumer>()
    .ConfigureEndpoint(e => e.MaxConcurrency(3));
```

In a multi-transport setup, `Handler<T>()` also claims the handler for this transport, overriding the default:

C#

```
builder.Services
    .AddMessageBus()
    .AddEventHandler<OrderPlacedEventHandler>()
    .AddEventHandler<AuditHandler>()
    .AddRabbitMQ(r => r.IsDefaultTransport())
    .AddInMemory(m => m.Handler<AuditHandler>());
// OrderPlacedEventHandler → RabbitMQ (default)
// AuditHandler → InMemory (claimed)
```

## Next steps

- [RabbitMQ Transport](https://chillicream.com/docs/mocha/transports/rabbitmq) \- Configure the RabbitMQ transport for production deployments.
- [Handlers and Consumers](https://chillicream.com/docs/mocha/handlers-and-consumers) \- Learn about every handler type and how to register them.
[Edit this page on GitHub](https://github.com/ChilliCream/graphql-platform/edit/main/website/content/docs/mocha/transports/in-memory.md)

Last updated on **June 30, 2026** by **Tobias Tengler**
