Events
Events let a service notify other nodes that something happened and, optionally, run a handler on those nodes when the change is received.
Define an event
Section titled “Define an event”Mark an event class with @Event. The decorator records the class name as the
event kind and adds serialize() / deserialize() helpers.
import { Event } from '@calimero-network/calimero-sdk-js';
@Eventexport class ItemAdded { constructor( public key: string, public value: string, public timestamp: number ) {}}
@Eventexport class ItemRemoved { constructor(public key: string) {}}Emit an event
Section titled “Emit an event”There are two emitters, both imported from the package root:
import { emit, emitWithHandler } from '@calimero-network/calimero-sdk-js';
// Fire-and-forget notification (no handler runs on receivers)emit(new ItemAdded('key1', 'value1', Date.now()));
// Emit and name a handler method to run on receiving nodesemitWithHandler(new ItemAdded('key1', 'value1', Date.now()), 'onItemAdded');The event payload is serialized through the ABI manifest, so it is compatible with Rust services that observe the same event type.
Handle an event
Section titled “Handle an event”A handler is just a method on the logic class whose name matches the string
passed to emitWithHandler:
@Logic(MyApp)export class MyAppLogic extends MyApp { addItem(key: string, value: string): void { this.items.set(key, value); emitWithHandler(new ItemAdded(key, value, Date.now()), 'onItemAdded'); }
// Runs on receiving nodes after the delta is applied onItemAdded(event: ItemAdded): void { this.itemCount.increment(); }}Event flow
Section titled “Event flow”Handlers must stay convergent
Section titled “Handlers must stay convergent”Handlers may run on many nodes, in any order, and can be retried. To keep every node’s state identical, a handler must be:
- Commutative — order-independent.
this.userCount.increment()(aCounter) is safe; reading another key and appending to it assumes an ordering that may not hold. - Independent — no unsynchronized shared state. Two handlers writing the same key race; writing distinct keys is fine.
- Idempotent — safe to run more than once. CRDT increments are; charging an external API is not.
- Pure — no external side effects. Mutating CRDT state and
env.log(...)are fine; outbound HTTP calls are not (and are not available in the sandbox).
Best practices
Section titled “Best practices”- Keep handlers to a single, commutative state update.
- Split complex reactions into multiple events rather than one big handler.
- Test handlers under concurrent, out-of-order delivery — that is the real execution model.
See also
Section titled “See also”- Collections — the CRDT types handlers should mutate.
- API Reference —
emit/emitWithHandlersignatures.