Cookbook
A cookbook of the most common postboy patterns — copy, paste, adjust. For full walkthrough applications see the ecosystem guides: Angular, React, Vue; for testing recipes see Testing recipes.
All snippets assume an imported bus (import { ... } from '@artstesh/postboy') and messages declaring their own unique static readonly ID.
1. Fire-and-forget event
export class SectionSelectedEvent extends PostboyGenericMessage {
static readonly ID = 'app.section.selected';
constructor(public sectionId: string) {
super();
}
}
// register (registrator's _up), then:
this.postboy.sub(SectionSelectedEvent)
.subscribe((ev) => this.select(ev.sectionId));
this.postboy.fire(new SectionSelectedEvent('section-2'));
2. Request / response
export class GetItemsQuery extends PostboyCallbackMessage<Item[]> {
static readonly ID = 'app.items.get';
constructor(public filter: FilterSettings) {
super();
}
}
// responder
this.postboy.sub(GetItemsQuery).subscribe((ev) => ev.finish(this.loadItems(ev.filter)));
// requester
this.postboy.fireCallback(new GetItemsQuery(filter), (items) => (this.items = items));
Intermediate results are possible: emit partial data with ev.next(partial) and complete with ev.finish(final). See Callbacks and Callback patterns.
3. Synchronous command
export class MultiplyExecutor extends PostboyExecutor<number> {
static readonly ID = 'app.math.multiply';
constructor(public x: number, public y: number) {
super();
}
}
// registration — the registrator's chainable method, not the deprecated PostboyService one
this.recordExecutor(MultiplyExecutor, (e) => e.x * e.y);
// usage — synchronous, returns the value directly
const result = this.postboy.exec(new MultiplyExecutor(2, 5)); // 10
See Executors for the full command model.
4. State and history
// Current state — new subscribers immediately receive the latest value
this.recordBehavior(ThemeEvent, new ThemeEvent({ mode: 'dark' }));
// Short history — late subscribers receive the last search query
this.recordReplay(SearchQueryEvent, 1);
The initial value of recordBehavior is a message instance. See Registration and the registrators reference.
5. A feature with lifecycle
export class FeatureRegistrator extends PostboyAbstractRegistrator {
constructor(postboy: PostboyService, private service: FeatureService) {
super(postboy);
this.registerServices([service]); // service.up() runs after the registrations
}
protected _up(): void {
this.recordSubject(FeatureStartedEvent);
this.recordExecutor(FeatureCommand, (e) => this.service.run(e));
}
}
// mount
this.registrator.up();
// unmount — disconnects everything recorded above and calls services' down()
this.registrator.down();
6. A short-lived scope without a registrator class
const scope = this.postboy.exec(new AddNamespace('quick-scope'));
scope.recordSubject(DialogOpenedEvent);
// ... later, one call tears it all down:
this.postboy.exec(new EliminateNamespace('quick-scope'));
See Namespaces.
7. Gating a command with middleware
class AdminOnlyMiddleware extends PostboyMiddleware {
canHandle(context: PipelineContext): boolean {
return context.message.id === AdminCommand.ID;
}
before(context: PipelineContext): MiddlewareDecision {
return this.auth.isAdmin()
? { type: MiddlewareDecisionType.Continue }
: { type: MiddlewareDecisionType.Interrupt };
}
}
this.postboy.exec(new AddMiddleware(new AdminOnlyMiddleware(this.auth)));
Call sites catch the structured CancelError where gating is expected. See Middleware and Middleware cancellation.
8. Muting a noisy stream
this.postboy.exec(new LockMessage(TelemetryEvent)); // silently skip delivery
// ... heavy work ...
this.postboy.exec(new UnlockMessage(TelemetryEvent));
9. Manual one-off registration
this.postboy.exec(new ConnectMessage(TemporaryEvent, new Subject<TemporaryEvent>()));
// ...
this.postboy.exec(new DisconnectMessage(TemporaryEvent.ID)); // completes the stream
10. A quick test
const world = new PostboyWorld();
const service = new MyService(world.postboy); // feed the recording mock
world.given.executor(GetDataQuery, 42);
service.doWork();
world.then.fired(DataLoadedEvent).once();
world.dispose();
See Testing quick start for the full Given/When/Then toolkit.
07 September 2026