Skip to main content
The @feathersjs/socketio package provides real-time functionality using Socket.io, enabling bidirectional communication between clients and your Feathers services.

Installation

npm install @feathersjs/socketio socket.io

Basic Setup

1

Import Socket.io configuration

Import the Socket.io configurator from the package:
import socketio from '@feathersjs/socketio'
import { feathers } from '@feathersjs/feathers'

const app = feathers()
2

Configure the transport

Add Socket.io to your application:
app.configure(socketio())
3

Register services

Services are automatically available through Socket.io:
app.use('/messages', {
  async find() {
    return [{ id: 1, text: 'Hello' }]
  },
  async create(data) {
    return { id: 2, ...data }
  }
})
4

Start the server

Listen on a port to start the Socket.io server:
await app.listen(3030)
console.log('Socket.io server running on http://localhost:3030')

Complete Example

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

const app = feathers()

// Configure Socket.io
app.configure(socketio())

// Register a service
app.use('/messages', {
  async find() {
    return [
      { id: 1, text: 'Hello world' },
      { id: 2, text: 'How are you?' }
    ]
  },

  async create(data) {
    const message = { id: Date.now(), ...data }
    return message
  }
})

// Start server
const server = await app.listen(3030)
console.log('Feathers Socket.io server started on port 3030')

With Express Integration

Socket.io works seamlessly with Express:
import { feathers } from '@feathersjs/feathers'
import express, { rest, json } from '@feathersjs/express'
import socketio from '@feathersjs/socketio'

const app = express(feathers())

// HTTP middleware
app.use(json())

// Configure both REST and Socket.io
app.configure(rest())
app.configure(socketio())

// Services work over both transports
app.use('/messages', messageService)

await app.listen(3030)
// Now available via:
// - HTTP: GET http://localhost:3030/messages
// - Socket.io: socket.emit('find', 'messages', {})

Socket.io Configuration

Customize Socket.io server options:
Pass Socket.io server options directly:
app.configure(socketio({
  cors: {
    origin: 'https://example.com',
    credentials: true
  },
  pingTimeout: 60000,
  pingInterval: 25000
}))

Middleware

Socket.io middleware runs when clients connect:
import socketio, { FeathersSocket, NextFunction } from '@feathersjs/socketio'

app.configure(socketio((io) => {
  // Authentication middleware
  io.use((socket: FeathersSocket, next: NextFunction) => {
    const token = socket.handshake.auth.token

    if (!token) {
      return next(new Error('Authentication required'))
    }

    // Add user info to socket params
    socket.feathers = {
      ...socket.feathers,
      user: { id: 123, token }
    }

    next()
  })

  // Logging middleware
  io.use((socket: FeathersSocket, next: NextFunction) => {
    console.log('Client connected:', socket.id)
    socket.on('disconnect', () => {
      console.log('Client disconnected:', socket.id)
    })
    next()
  })
}))

Socket Parameters

Customize parameters passed to services from socket connections:
app.configure(socketio((io) => {
  io.use((socket: FeathersSocket, next: NextFunction) => {
    // Extract query parameters from handshake
    const { channel, apiKey } = socket.handshake.query

    socket.feathers = {
      provider: 'socketio',
      headers: socket.handshake.headers,
      channel,
      apiKey
    }

    next()
  })
}))

// Services receive these params
app.use('/messages', {
  async find(params) {
    console.log(params.channel)  // From socket.handshake.query
    console.log(params.apiKey)   // From socket.handshake.query
    console.log(params.provider) // 'socketio'
    return []
  }
})

Authentication

Integrate authentication with Socket.io:
// Authentication is handled automatically
// when @feathersjs/authentication is configured
import socketio from '@feathersjs/socketio'
import { AuthenticationService } from '@feathersjs/authentication'

app.configure(socketio())

// Authentication parses JWT from handshake automatically

Client Usage

Connect from the browser:
import io from 'socket.io-client'

const socket = io('http://localhost:3030')

// Call service methods
socket.emit('find', 'messages', {}, (error, result) => {
  if (error) {
    console.error(error)
  } else {
    console.log('Messages:', result)
  }
})

socket.emit('create', 'messages', { text: 'Hello!' }, (error, message) => {
  console.log('Created:', message)
})

// Listen to events
socket.on('messages created', (message) => {
  console.log('New message:', message)
})

Real-Time Events

Services automatically emit real-time events:
app.use('/messages', messageService)

// Configure event channels
app.service('messages').publish((data, context) => {
  // Send to all connected clients
  return app.channel('everybody')
})

app.on('connection', (connection) => {
  // Add connection to channel
  app.channel('everybody').join(connection)
})

app.on('disconnect', (connection) => {
  // Remove from channels on disconnect
  app.channel('everybody').leave(connection)
})
Clients receive these events:
// Client-side
socket.on('messages created', (message) => {
  console.log('New message created:', message)
})

socket.on('messages patched', (message) => {
  console.log('Message updated:', message)
})

socket.on('messages removed', (message) => {
  console.log('Message deleted:', message)
})

Connection Management

Track and manage socket connections:
app.configure(socketio((io) => {
  io.on('connection', (socket) => {
    console.log('New connection:', socket.id)

    // Access Feathers params
    const params = socket.feathers
    console.log('Connection params:', params)

    socket.on('disconnect', (reason) => {
      console.log('Disconnected:', socket.id, reason)
    })
  })
}))

// Listen to Feathers connection events
app.on('connection', (connection) => {
  console.log('Feathers connection established')

  // Add to channels
  app.channel('online').join(connection)

  if (connection.user) {
    app.channel(`user/${connection.user.id}`).join(connection)
  }
})

app.on('disconnect', (connection) => {
  console.log('Feathers connection closed')
})

Channels and Publishing

Control which connections receive real-time events:
app.service('messages').publish('created', (data, context) => {
  // Send to specific channels
  return [
    app.channel('everybody'),
    app.channel(`room/${data.roomId}`)
  ]
})

app.service('messages').publish('patched', (data, context) => {
  // Send only to the creator and admins
  return [
    app.channel(`user/${data.userId}`),
    app.channel('admins')
  ]
})

// Dynamic channels
app.on('login', (authResult, { connection }) => {
  const { user } = authResult

  app.channel('authenticated').join(connection)

  if (user.role === 'admin') {
    app.channel('admins').join(connection)
  }

  // Join room channels
  user.rooms?.forEach((roomId) => {
    app.channel(`room/${roomId}`).join(connection)
  })
})

Custom Events

Send custom events to clients:
app.configure(socketio((io) => {
  // Broadcast custom events
  setInterval(() => {
    io.emit('server-time', { time: new Date() })
  }, 1000)

  io.on('connection', (socket) => {
    // Listen to custom events from clients
    socket.on('ping', () => {
      socket.emit('pong')
    })
  })
}))

Error Handling

Handle errors in Socket.io connections:
app.configure(socketio((io) => {
  io.use((socket, next) => {
    try {
      // Validation logic
      if (!socket.handshake.auth.token) {
        throw new Error('Token required')
      }
      next()
    } catch (error) {
      next(error)
    }
  })

  io.on('connection', (socket) => {
    socket.on('error', (error) => {
      console.error('Socket error:', error)
    })
  })
}))

// Service errors are sent back to the client
app.use('/messages', {
  async get(id) {
    throw new Error('Something went wrong')
  }
})

// Client receives the error in the callback
socket.emit('get', 'messages', 1, (error, result) => {
  if (error) {
    console.error('Error:', error.message)
  }
})

TypeScript Support

import { feathers, Service } from '@feathersjs/feathers'
import socketio, { FeathersSocket, NextFunction } from '@feathersjs/socketio'
import { Server } from 'socket.io'

interface Message {
  id: number
  text: string
  userId: number
}

interface ServiceTypes {
  messages: Service<Message>
}

const app = feathers<ServiceTypes>()

app.configure(socketio((io: Server) => {
  io.use((socket: FeathersSocket, next: NextFunction) => {
    socket.feathers = {
      provider: 'socketio',
      headers: socket.handshake.headers
    }
    next()
  })
}))

app.use('/messages', {
  async find() {
    return [{ id: 1, text: 'Hello', userId: 1 }]
  }
})

API Reference

socketio(callback?)

Configures Socket.io transport. Signature:
socketio(callback?: (io: Server) => void)
socketio(options: Partial<ServerOptions>, callback?: (io: Server) => void)
socketio(port: number, options?: Partial<ServerOptions>, callback?: (io: Server) => void)
Parameters:
  • port - Optional port number for Socket.io server
  • options - Socket.io ServerOptions
  • callback - Function called with Socket.io server instance

Middleware Types

FeathersSocket: Extended Socket.io socket with Feathers params:
interface FeathersSocket extends Socket {
  feathers?: Params & { [key: string]: any }
}
NextFunction:
type NextFunction = (err?: any) => void

Performance Tips

  1. Set Max Listeners: Prevent memory leaks with many services
    app.configure(socketio((io) => {
      io.sockets.setMaxListeners(64)
    }))
    
  2. Use Channels Efficiently: Only send events to relevant clients
    app.service('messages').publish((data) => {
      return app.channel(`room/${data.roomId}`)
    })
    
  3. Enable Compression: Reduce bandwidth usage
    app.configure(socketio({
      perMessageDeflate: true
    }))
    
  4. Adjust Timeouts: Tune for your use case
    app.configure(socketio({
      pingTimeout: 60000,
      pingInterval: 25000
    }))