Getting started
Installation
Like any other package, you can install Missive.js using npm, yarn or pnpm.
pnpm add missive.js
npm install missive.js
yarn add missive.js
Ubiquitous language
- Intent: This is what you want to send to the bus. It can be a
command
, aquery
or anevent
. - Envelope: It gives full flexibility inside the service bus, by wrapping the messages into it, allowing to add useful information inside through envelope stamps.
- Stamps: Piece of information you need to attach to an Envelope, can be any sort of metadata your middleware / handler may use.
Create your first handler
Here we are going to create an handler aside its definitions.
// the Types to use in the bus configurationtype Command = { email: string };type Result = Awaited<ReturnType<typeof handler>>;export type Definition = CommandHandlerDefinition<'createUser', Command, Result>;
// the handler to handle the intentconst handler = async (envelope: Envelope<Command>, deps: Deps) => { const { email } = envelope.message; await deps.mailer('subject', email, 'plop', { html: 'html', text: 'text' }); return { success: true };};
// the factory to create the handler with the dependencies you may needexport const Factory = (deps: Deps) => (query: Envelope<Command>) => handler(query, deps);
More examples are available on Github.
Define your bus(es)
Once you have your handler(s) ready, you can create your bus(es) and register your handler(s).
type CommandHandlerRegistry = SendRegistrationEmailDefinition & SendNotificationDefinition;const bus = createCommandBus<CommandHandlerRegistry>();
bus.register('sendRegistrationEmail', Factory({ mailer }));
The bus will add missive:identity
stamp on the envelope to identify it on dispatch. If you want to provide your own uuid genenrator, you can pass it:
const queryBus: QueryBus = createQueryBus<QueryHandlerRegistry>({ options: { randomUUID: async () => nanoid(), }});
More examples are available on Github.
Dispatch an intent
From there you can use the preferred approach to inject the bus(es) in your application and dispatch an intent.
const intent = bus.createCommand('sendRegistrationEmail', { email: 'plopix@example.com' });// ...const { envelope, result } = await bus.dispatch(intent);
Enjoy fully typed code and a clean architecture!
More examples are available on Github.
Added Stamps
The Bus is going to add some Stamps to the Envelope, here is the list:
-
export type IdentityStamp = Stamp<{ id: string }, 'missive:identity'>;
To identify an Envelope
-
export type HandledStamp<R> = Stamp<R, 'missive:handled'>;
To includes the result(s) of the handler(s)
How does it work?
With Missive.js, an intent is either a command
, a query
, or an event
and it is created using the specialized bus.
Usually you will have 3 different buses:
- Query Bus: For read-only operations that retrieve data.
- Command Bus: For operations that mutate application state.
- Event Bus: For broadcasting changes and triggering asynchronous actions.
Here’s how it works:
-
You begin by creating an intent. This could be a
command
(e.g., to create a new user), a query (e.g., to fetch product details), or an event (e.g., an order being placed). At the point of creation, the intent is validated to ensure it has the correct structure and necessary parameters, meeting all predefined requirements. -
Once returned (or later in the lifecycle of your application), you dispatch the intent to the bus.
-
On dispatch, the bus wraps the intent in an Envelope.
-
The Envelope then flows through the series of configured middleware functions. These middleware can:
- log relevant information for monitoring
- apply additional business logic or validations as needed
- attach Stamps to the Envelope. Stamps serve as markers influencing how the intent is processed.
- observe the result of the
command
and dispatch events based on the outcome.
-
Finally, the Envelope reaches the handler defined for the Intent. The handler processes the intent based on the information and stamps within the Envelope, and returns the result, which could be data, a confirmation, or any other type of response.
This structured pipeline ensures that intents are processed in a controlled and consistent manner, allowing for flexibility in adding features like logging, validation, and custom behavior, all while keeping the core business logic clean and maintainable.
Here is a diagram to illustrate the flow of an intent through Missive.js:

Missive.js. MIT License.
Powered by Astro Starlight.
Inspired by Symfony Messenger