Postie
The Idea
Every CQRS application has a thin layer of glue between HTTP and its handlers: an endpoint binds a request, hands it to a mediator, and shapes the response. It's only a few lines per endpoint, but they're lines that have to be right — the right status code, the right Location header, the right binding — and they're repeated everywhere. Postie is a set of .NET packages that delete that layer: CQRS commands and queries map straight to ASP.NET Core minimal API endpoints, with the REST conventions — 201 with a Location header, 404 for a null query result, 204 for no-content commands, hybrid route-plus-body binding — applied for you.
From Asm.Cqrs to Postie
The idea was born inside the ASM Library. Asm.Cqrs was a thin layer on top of MediatR, and Asm.Cqrs.AspNetCore took it one step further by binding API endpoints directly to commands and queries. That last step was always the part that surprised me with how clean the resulting code reads — you write the handler, you map the route, and the translation layer simply isn't there any more.
Then the ground shifted. MediatR moved to a commercial licence, and a thin layer over someone else's mediator suddenly had that licence baked into its foundations. I could have just swapped mediators — but the more interesting realisation was that the part of Asm.Cqrs worth keeping had never been the mediator at all. It was the last mile between HTTP and the handlers.
So Postie inverts the dependency. The endpoint mapping engine is the product, it never references a mediator, and dispatch goes through one small abstraction — IEndpointDispatcher — that any mediator can stand behind: Postie's own lightweight in-box mediator, MediatR itself (keeping your existing IRequest types, pinned to the 12.x Apache-2.0 line so it stays free by default), or anything you care to implement in two methods. No mediator can hold the endpoint layer hostage again — including mine.
Design
A second principle emerged during development: Postie only advertises what it produces. Endpoint mappings declare exactly the OpenAPI responses their own code can emit — nothing speculative — and anything conditional is yours to compose on the returned RouteHandlerBuilder. Misconfiguration fails fast: map a body-bound command carrying [FromRoute] attributes and Postie throws at startup, naming the offending members, rather than silently binding wrong data per request.
What's Inside
Postie.AspNetCore
The endpoint mapping engine — MapQuery, MapCommand, MapPostCreate and friends — mediator-agnostic by construction.
Postie.Cqrs
The in-box mediator: separate command and query dispatchers, pipeline behaviours, streaming queries, and OpenTelemetry tracing on every dispatch, built on cached compiled delegates rather than per-call reflection.
Adapters and validation
Postie.Cqrs.AspNetCore wires the in-box mediator to the engine; Postie.AspNetCore.MediatR does the same for MediatR. Postie.Cqrs.FluentValidation validates commands and queries before they reach a handler, and Postie.AspNetCore.FluentValidation turns validation failures into RFC 9457 problem-details responses.
Free Forever
Postie is MIT licensed with a standing commitment: no commercial edition, no licence keys, no revenue thresholds. Given the project exists partly because a dependency's licence changed underneath it, this isn't a slogan — it's the design brief.
Dog Food (Nom Nom!)
Following the same philosophy as everything else I build, Postie earns its keep in my own applications first — starting with MooBank — with the intention that it supersedes Asm.Cqrs across my projects, completing the evolution. Two runnable sample applications live in the repository (the same Orders API built once on the in-box mediator and once on MediatR with validation and OpenAPI), and every package carries its own README, tests across three target frameworks, and XML documentation on the public surface.