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:
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:
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:
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
| Feature | Local Transport |
|---|---|
| Execution | Runs in-process on the emitting instance. |
| Ordering | Sequential execution in registration order. |
| Error Isolation | Per listener. An exception in one listener does not affect others. |
| Durability | In-memory. For distributed brokers, see Custom Adapter. |