Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/feathersjs/feathers/llms.txt

Use this file to discover all available pages before exploring further.

Feathers services automatically emit events when resources are created, updated, patched, or removed. This allows you to build real-time applications by listening to these events through various transports.

Default Events

Services automatically emit the following events:
  • created - Emitted after a resource is created
  • updated - Emitted after a resource is updated
  • patched - Emitted after a resource is patched
  • removed - Emitted after a resource is removed
These events are emitted on both the service and the application.

Event Listeners

Services extend Node’s EventEmitter, so you can use all standard EventEmitter methods:

on()

Listen for an event:
service.on(event: string, listener: (data: any, context?: HookContext) => void)
event
string
required
The event name (e.g., 'created', 'patched')
listener
function
required
Callback function that receives the event data and optional hook context
const userService = app.service('users')

userService.on('created', (user, context) => {
  console.log('New user created:', user)
  console.log('Created by:', context.params.user)
})

once()

Listen for an event once:
service.once(event: string, listener: (data: any, context?: HookContext) => void)
userService.once('created', (user) => {
  console.log('First user created:', user)
})

removeListener()

Remove a specific event listener:
service.removeListener(event: string, listener: Function)
const handleCreated = (user) => {
  console.log('User created:', user)
}

userService.on('created', handleCreated)
userService.removeListener('created', handleCreated)

removeAllListeners()

Remove all listeners for an event or all events:
service.removeAllListeners(event?: string)
// Remove all 'created' listeners
userService.removeAllListeners('created')

// Remove all listeners
userService.removeAllListeners()

Event Emission

Events are automatically emitted after successful service method calls unless:
  1. The service’s events option includes the event name (indicating the service handles emission itself)
  2. The context.event property is set to null in a hook

Automatic Event Emission

// This will emit a 'created' event
const user = await app.service('users').create({
  name: 'John Doe',
  email: 'john@example.com'
})

// Listen for the event
app.service('users').on('created', (user) => {
  console.log('User created:', user)
})

Controlling Event Emission

You can control event emission in hooks:
// Prevent event emission
const skipEvent = async (context) => {
  context.event = null
}

// Change the event name
const customEvent = async (context) => {
  context.event = 'user-registered'
}

service.hooks({
  after: {
    create: [customEvent]
  }
})

service.on('user-registered', (user) => {
  console.log('Custom event:', user)
})

Custom Events

You can define custom events when registering a service:
app.use('/messages', messageService, {
  events: ['typing', 'delivered', 'read']
})

// Emit custom events
app.service('messages').emit('typing', {
  userId: 123,
  messageId: 456
})

// Listen for custom events
app.service('messages').on('typing', (data) => {
  console.log('User typing:', data)
})
events
string[]
List of custom event names. These are merged with the default events (created, updated, patched, removed).

Service Events Configuration

You can completely replace the default events:
app.use('/notifications', notificationService, {
  serviceEvents: ['sent', 'failed'] // Only these events, no defaults
})
serviceEvents
string[]
Complete list of events for this service. Unlike events, this replaces the default events entirely.

Event Data

Events receive two arguments:
  1. data - The resource that was created, updated, patched, or removed
  2. context - The hook context (optional)
service.on('created', (user, context) => {
  console.log('Created user:', user)
  console.log('Service path:', context.path)
  console.log('Method:', context.method)
  console.log('Params:', context.params)
})

Multiple Results

When a method returns multiple results (e.g., create with an array), the event is emitted once for each result:
const users = await app.service('users').create([
  { name: 'John' },
  { name: 'Jane' }
])

// 'created' event is emitted twice, once for each user
app.service('users').on('created', (user) => {
  console.log('User created:', user.name)
})
// Output:
// User created: John
// User created: Jane

Application-Level Events

The application itself is an EventEmitter and can be used for custom application events:
app.on('login', (user) => {
  console.log('User logged in:', user)
})

app.emit('login', { id: 1, name: 'John' })

Real-Time Transports

Events are the foundation for real-time updates in Feathers. When using transports like Socket.io or Primus, service events are automatically sent to connected clients.

Server-Side

import { feathers } from '@feathersjs/feathers'
import socketio from '@feathersjs/socketio'

const app = feathers()
app.configure(socketio())

app.use('/messages', {
  async create(data) {
    return { id: 1, ...data }
  }
})

Client-Side

import io from 'socket.io-client'
import { feathers } from '@feathersjs/feathers'
import socketio from '@feathersjs/socketio-client'

const socket = io('http://localhost:3030')
const app = feathers()
app.configure(socketio(socket))

const messageService = app.service('messages')

// Listen for real-time events
messageService.on('created', (message) => {
  console.log('New message:', message)
})

// Create a message (will trigger 'created' event for all connected clients)
await messageService.create({ text: 'Hello World' })

Event Filtering

For real-time applications, you often want to filter which clients receive which events. This is done through channels (see Publishing & Channels documentation).
app.on('connection', (connection) => {
  app.channel('authenticated').join(connection)
})

app.publish('created', (data, context) => {
  return app.channel('authenticated')
})

Event Hook

Feathers uses an internal eventHook to handle event emission:
export function eventHook(context: HookContext, next: NextFunction) {
  const { events } = getServiceOptions(context.self)
  const defaultEvent = defaultEventMap[context.method] || null
  
  context.event = defaultEvent
  
  return next().then(() => {
    if (typeof context.event === 'string' && !events.includes(context.event)) {
      const results = Array.isArray(context.result) 
        ? context.result 
        : [context.result]
      
      results.forEach((element) => 
        context.self.emit(context.event, element, context)
      )
    }
  })
}

Examples

Audit Logging

const auditLog = []

app.service('users').on('created', (user, context) => {
  auditLog.push({
    action: 'created',
    resource: 'user',
    resourceId: user.id,
    userId: context.params.user?.id,
    timestamp: new Date()
  })
})

Send Notifications

app.service('tasks').on('created', async (task) => {
  if (task.assignedTo) {
    await app.service('notifications').create({
      userId: task.assignedTo,
      message: `New task assigned: ${task.title}`
    })
  }
})

Update Caches

const cache = new Map()

app.service('products').on('patched', (product) => {
  cache.set(product.id, product)
})

app.service('products').on('removed', (product) => {
  cache.delete(product.id)
})

Webhook Integration

app.service('orders').on('created', async (order) => {
  await fetch('https://external-api.com/webhook', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      event: 'order.created',
      data: order
    })
  })
})

Type Definitions

interface ServiceAddons<A = Application, S = Service> extends EventEmitter {
  id?: string
  hooks(options: HookOptions<A, S>): this
}

type FeathersService<A = FeathersApplication, S = Service> = 
  S & ServiceAddons<A, S>

// Default event mapping
const defaultEventMap = {
  create: 'created',
  update: 'updated',
  patch: 'patched',
  remove: 'removed'
}

// Default events
const defaultServiceEvents = ['created', 'updated', 'patched', 'removed']