Events

Events

Emit typed domain events from VitNode plugins and subscribe with isolated, typed event listeners.

VitNode includes an in-process event bus accessible via c.get("events"). Events decouple features across plugins (e.g. sending a welcome email when a user registers) without direct inter-plugin dependencies.

Quick start

1. Emit an Event

Emit events from any Hono route or service after a database write:

await c.get("events").emit("user.created", {
  userId: user.id,
  email: user.email,
  name: user.name,
  emailVerified: user.emailVerified,
})

2. Subscribe with an Event Listener

Define a listener in your plugin:

plugins/shop/src/api/lib/events.ts
import { buildEventListener } from "@vitnode/core/api/lib/events"

export const welcomeListener = buildEventListener({
  event: "user.created",
  name: "send-welcome-email",
  handler: async (c, payload) => {
    await c.get("queue").dispatch({
      name: "send-welcome-email",
      payload: { userId: payload.userId, email: payload.email },
    })
  },
})

Register the listener in your module's events array:

plugins/shop/src/api/modules/orders/orders.module.ts
import { buildModule } from "@vitnode/core/api/lib/module"
import { welcomeListener } from "../../lib/events"

export const ordersModule = buildModule({
  name: "orders",
  routes: [listOrdersRoute],
  events: [welcomeListener], 
})

Declare Custom Event Types

Extend VitNode's global EventMap interface in your plugin so payload types are strictly enforced and autocompleted:

plugins/shop/src/api/lib/events.ts
export interface OrderPlacedPayload {
  orderId: number
  userId: number
  total: number
}

declare module "@vitnode/core/lib/events" {
  interface EventMap {
    "shop.order.placed": OrderPlacedPayload
  }
}

Now emit("shop.order.placed", ...) and buildEventListener({ event: "shop.order.placed", ... }) are strictly type-checked.


Delivery Guarantees

FeatureLocal Transport
ExecutionRuns in-process on the emitting instance.
OrderingSequential execution in registration order.
Error IsolationPer listener. An exception in one listener does not affect others.
DurabilityIn-memory. For distributed brokers, see Custom Adapter.

Learn More