Event Bus

The event bus enables decoupled inter-module communication. Modules emit events, other modules react — without importing each other.

Emitting Events

From any controller, use the emit function from the request context:

export async function login(request, services) {
  const { body, send, emit } = request
  const { authService } = services

  const result = await authService.login(body.email, body.password)
  await emit('auth:login', { userId: result.data.user.id })
  send(result.data)
}

Listening for Events

In a module's module.js, declare event listeners in the on property:

// modules/notifications/module.js
export default {
  name: 'notifications',
  prefix: '/notifications',

  on: {
    'order:created': 'onOrderCreated',
    'auth:login':    'onUserLogin',
  },

  routes: [],
}

The handler function receives event data + services:

// modules/notifications/notifications.controller.js

export function onOrderCreated(data, services) {
  console.log('New order!', data.orderId, data.total)
  // Send email, push notification, etc.
}

export function onUserLogin(data, services) {
  console.log('User logged in:', data.userId)
}

Manual EventBus Usage

import { EventBus } from 'SpaceNode'

const bus = new EventBus()
bus.on('test', (data) => console.log(data))
bus.once('test', (data) => console.log('only once'))
await bus.emit('test', { hello: 'world' })
bus.off('test')
bus.clear()

EventBus API

MethodDescription
on(event, fn)Subscribe to an event
once(event, fn)Subscribe once (auto-removes after first emit)
emit(event, data)Emit event (sequential execution of listeners)
off(event, fn?)Unsubscribe a specific handler or all for that event
clear()Remove all listeners
listEvents()Get all registered event names
destroy()Remove all listeners and prevent future subscriptions

Programmatic Access via app.events

You can subscribe to events directly on the app instance using app.events:

const app = await createApp()

app.events.on('user:registered', async (data) => {
  await sendWelcomeEmail(data.email)
})
When to use — Use app.events for programmatic subscriptions in your entry file or setup code. Use the on property in module.js for declarative, module-scoped event listeners.

Error Handling

If an event handler throws an error, it is not swallowed. SpaceNode catches the error, logs it, and re-emits it as an event:error event:

app.events.on('event:error', ({ event, errors }) => {
  console.error(`Errors in event "${event}":`, errors)
})

The event:error payload contains:

Best practice — Always subscribe to event:error in production to avoid silent failures in event handlers.