All skills

WebSocket and realtime communication with sails-hook-sockets for Sails.js applications. Use this skill when building chat, notifications, live updates, presence tracking, or any feature that requires persistent bidirectional communication between server and client.

Use this Skill: https://skilld.dev/gh/sailscastshq/boring-stack/realtime

This session only. Nothing lands on disk.

rulessails-sockets.md

≈2.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

sails.sockets API

The sails.sockets object provides low-level methods for room management and broadcasting. All methods are available after Sails lifts.

Room Management

sails.sockets.join(socket, roomName, [cb])

Subscribe a socket to a room. Sockets in a room receive messages broadcast to that room.

// Join using the request object (most common)
sails.sockets.join(req, 'chat-general')

// Join using a socket object directly
sails.sockets.join(req.socket, 'chat-general')

// Join using a socket ID string (works cross-server with Redis)
sails.sockets.join('abc123socketId', 'chat-general')

// With callback
sails.sockets.join(req, 'chat-general', (err) => {
  if (err) {
    return res.serverError(err)
  }
  return res.ok()
})

Parameters:

Param Type Description
socket req, socket, or string The socket to subscribe (request object, socket object, or socket ID)
roomName string Name of the room to join
cb function Optional callback (err)

A socket can be in multiple rooms simultaneously. Room names are arbitrary strings.

sails.sockets.leave(socket, roomName, [cb])

Unsubscribe a socket from a room.

sails.sockets.leave(req, 'chat-general')

// With callback
sails.sockets.leave(req, 'chat-general', (err) => {
  if (err) {
    return res.serverError(err)
  }
  sails.log.info('Left the room')
})

Parameters: Same as join().

sails.sockets.leaveAll(roomName, [cb])

Remove all sockets from a room and unsubscribe them.

// Remove everyone from the room
sails.sockets.leaveAll('chat-general')

// With callback
sails.sockets.leaveAll('chat-general', (err) => {
  if (err) {
    return res.serverError(err)
  }
  sails.log.info('Room cleared')
})

Useful for cleanup when a chat room is deleted or a session ends.

sails.sockets.addRoomMembersToRooms(sourceRoom, destRooms, [cb])

Subscribe all sockets in one room to one or more additional rooms.

// Everyone in 'team-alpha' also joins 'company-announcements'
sails.sockets.addRoomMembersToRooms('team-alpha', 'company-announcements')

// Add to multiple rooms at once
sails.sockets.addRoomMembersToRooms('team-alpha', ['announcements', 'events'])

Works cross-server when using Redis adapter.

sails.sockets.removeRoomMembersFromRooms(sourceRoom, destRooms, [cb])

Unsubscribe all sockets in one room from one or more other rooms.

// Remove everyone in 'team-alpha' from 'announcements'
sails.sockets.removeRoomMembersFromRooms('team-alpha', 'announcements')

// Remove from multiple rooms
sails.sockets.removeRoomMembersFromRooms('team-alpha', [
  'announcements',
  'events'
])

Broadcasting

sails.sockets.broadcast(roomNames, [eventName], data, [socketToOmit])

Send a message to all sockets in one or more rooms.

// Broadcast to a single room (default event name: 'message')
sails.sockets.broadcast('chat-general', { text: 'Hello!' })

// Broadcast with a custom event name
sails.sockets.broadcast('chat-general', 'newMessage', {
  text: 'Hello everyone!',
  sender: 'Alice',
  timestamp: Date.now()
})

// Broadcast to multiple rooms
sails.sockets.broadcast(['chat-general', 'chat-random'], 'announcement', {
  text: 'Server maintenance in 5 minutes'
})

// Broadcast but exclude the sender (pass req as last arg)
sails.sockets.broadcast(
  'chat-general',
  'newMessage',
  {
    text: 'Hello!',
    sender: 'Alice'
  },
  req
)

Parameters:

Param Type Description
roomNames string or string[] Room(s) to broadcast to
eventName string Event name (default: 'message')
data object Message payload
socketToOmit req or socket Socket that should NOT receive this message

Omitting the sender: The last argument pattern is the most common way to prevent the sender from receiving their own message. Pass req (the request object) as the last argument:

// In an action that handles a chat message:
module.exports = {
  fn: async function () {
    const message = await Message.create({
      text: this.req.param('text'),
      sender: this.req.session.userId
    }).fetch()

    // Broadcast to everyone in the room EXCEPT the sender
    sails.sockets.broadcast('chat-general', 'newMessage', message, this.req)

    return message
  }
}

sails.sockets.blast([eventName], data, [socketToOmit])

Broadcast to ALL connected sockets in the default namespace (not scoped to a room).

// Blast to everyone
sails.sockets.blast('systemAlert', {
  message: 'Server restarting in 5 minutes'
})

// Blast with default event name ('message')
sails.sockets.blast({ type: 'maintenance', eta: 300 })

// Blast but exclude one socket
sails.sockets.blast('systemAlert', { message: 'Hello' }, req)

Use sparingly — blasting to all sockets is rarely what you want. Prefer broadcast() with specific rooms.

Socket Lookup

sails.sockets.getId(req)

Get the socket ID from a request object.

const socketId = sails.sockets.getId(req)
sails.log.info(`Socket ${socketId} connected`)

Returns undefined if the request is not a socket request.

sails.sockets.get(socketId)

Get a socket object by its ID.

const socket = sails.sockets.get(socketId)

Important: Only finds sockets connected to the current server. In a multi-server setup with Redis, use socket IDs with join()/leave() instead (they work cross-server).

sails.sockets.parseSocket(req)

Extract the socket object from a request:

const socket = sails.sockets.parseSocket(req)
if (socket) {
  // It's a socket request
}

Default Event Name

When eventName is omitted from broadcast() or blast(), the default event name is 'message':

// These are equivalent:
sails.sockets.broadcast('room', { text: 'hi' })
sails.sockets.broadcast('room', 'message', { text: 'hi' })

// Client listens on 'message' event:
io.socket.on('message', (data) => {
  console.log(data.text) // 'hi'
})

Common Room Naming Conventions

Use descriptive, namespaced room names:

// Per-user rooms (for notifications)
sails.sockets.join(req, `user-${userId}`)

// Per-resource rooms (for live updates)
sails.sockets.join(req, `order-${orderId}`)

// Feature-specific rooms
sails.sockets.join(req, `chat-${roomId}`)
sails.sockets.join(req, `dashboard-stats`)

// Role-based rooms
sails.sockets.join(req, 'admins')
sails.sockets.join(req, 'moderators')

Action Pattern: Subscribe + Broadcast

The typical pattern is: one action subscribes sockets to a room, another action broadcasts updates.

// api/controllers/orders/subscribe.js — Subscribe to order updates
module.exports = {
  inputs: {
    orderId: { type: 'string', required: true }
  },

  fn: async function ({ orderId }) {
    if (!this.req.isSocket) {
      return this.res.badRequest('WebSocket required')
    }

    const order = await Order.findOne({ id: orderId })
    if (!order) {
      return this.res.notFound()
    }

    sails.sockets.join(this.req, `order-${orderId}`)
    return order
  }
}

// api/controllers/orders/update-status.js — Update and broadcast
module.exports = {
  inputs: {
    orderId: { type: 'string', required: true },
    status: { type: 'string', required: true }
  },

  fn: async function ({ orderId, status }) {
    const order = await Order.updateOne({ id: orderId }).set({ status })
    if (!order) {
      return this.res.notFound()
    }

    // Broadcast to all subscribers of this order
    sails.sockets.broadcast(`order-${orderId}`, 'orderUpdated', {
      id: order.id,
      status: order.status,
      updatedAt: order.updatedAt
    })

    return order
  }
}

Source: SKILL.md on GitHub

2 warnings17d4 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides a comprehensive guide and best practices for implementing realtime WebSocket features in Sails.js applications. It covers architectural concepts, room management, model-centric notifications, and client-side integration. The skill emphasizes security by documenting cross-site WebSocket hijacking protections and authorization patterns. No malicious behaviors or security risks were identified.

  • Socket17d

    1 alert: gptAnomaly

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    9/9 files flagged

Signed by skilld at bf19e10. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 8 months ago
Other metadata
metadata
{
  "author": "sailscastshq",
  "version": "1.0.0",
  "tags": "realtime, websockets, sockets, pubsub, socket.io, rooms, sails"
}

README badge

README badge for sailscastshq/boring-stack/realtime