Postboy Help

Quick Start

Ten minutes from zero to a working bus: define a message, register it, subscribe, fire it — then a synchronous command and an async query, with proper teardown.

1. Define a message

Every message class declares its own unique static readonly ID. The ID is the routing key — never inherit a parent's ID and never reuse an ID across classes.

import { PostboyGenericMessage } from '@artstesh/postboy'; export class OrderPlacedEvent extends PostboyGenericMessage { static readonly ID = 'shop.order.placed'; constructor(public readonly orderId: string) { super(); } }

2. Register, subscribe, fire

Register before use: fire throws for unregistered IDs.

import { PostboyService, ConnectMessage } from '@artstesh/postboy'; import { Subject } from 'rxjs'; const postboy = new PostboyService(); postboy.exec(new ConnectMessage(OrderPlacedEvent, new Subject<OrderPlacedEvent>())); postboy.sub(OrderPlacedEvent).subscribe((e) => { console.log('order placed:', e.orderId); }); postboy.fire(new OrderPlacedEvent('ORD-42'));

For anything beyond a demo, group registrations in a registrator — its down() disconnects everything at once.

3. A synchronous command

exec runs a registered handler and returns the result directly — it is synchronous, never await it.

import { PostboyExecutor, ConnectExecutor } from '@artstesh/postboy'; class GetUserNameExecutor extends PostboyExecutor<string> { static readonly ID = 'shop.get-user-name'; constructor(public readonly userId: string) { super(); } } postboy.exec(new ConnectExecutor(GetUserNameExecutor, (e) => users[e.userId].name)); const name: string = postboy.exec(new GetUserNameExecutor('u-1'));

4. An async query

For asynchronous results use PostboyCallbackMessage and fireCallback. The responder completes the message with finish(...).

import { PostboyCallbackMessage } from '@artstesh/postboy'; import { firstValueFrom } from 'rxjs'; class FetchOrderQuery extends PostboyCallbackMessage<Order> { static readonly ID = 'shop.fetch-order'; constructor(public readonly orderId: string) { super(); } } // responder side postboy.sub(FetchOrderQuery).subscribe((q) => q.finish(loadOrder(q.orderId))); // caller side const order = await firstValueFrom(postboy.fireCallback(new FetchOrderQuery('ORD-42')));

Note: without the second action argument, fireCallback dispatches lazily — the request is sent only when the returned Observable is subscribed. See Callbacks.

5. Tear down

Subjects are not auto-completed. Use a registrator's down() (or exec(new DisconnectMessage(OrderPlacedEvent.ID))) when a feature shuts down, and postboy.dispose() when the whole bus goes away.

Next steps

07 September 2026