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.

rulesgetting-started.md

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

Getting Started with Realtime

What is Realtime in Sails?

Traditional HTTP is request-response: the client asks, the server answers, the connection closes. Realtime communication uses WebSockets to maintain a persistent, bidirectional connection between client and server. This enables the server to push updates to the client at any time — without the client having to poll.

Sails provides realtime support through sails-hook-sockets, built on Socket.IO 4.x. It adds two powerful capabilities:

  1. Virtual requests — socket messages are routed through the same Sails router as HTTP requests, so the same actions handle both
  2. Room-based messaging — organize connected sockets into rooms for targeted broadcasting

Two-Tier Architecture

Sails offers two levels of realtime API:

Tier 1: sails.sockets (Low-Level)

Direct room management and broadcasting. Use this for custom messaging patterns.

// Join a socket to a room
sails.sockets.join(req, 'chat-room-42')

// Broadcast to everyone in the room
sails.sockets.broadcast('chat-room-42', 'newMessage', {
  text: 'Hello everyone!',
  sender: 'Alice'
})

Tier 2: Resourceful PubSub (High-Level)

Model-centric notifications. Subscribe sockets to specific records and publish changes automatically.

// Subscribe the requesting socket to changes on these users
User.subscribe(req, [userId])

// Later, when a user is updated, notify all subscribers
User.publish([userId], {
  verb: 'updated',
  data: { status: 'online' }
})

When to Use Which

Scenario Use
Chat rooms, custom channels sails.sockets
Typing indicators, cursor positions sails.sockets
Model CRUD notifications Resourceful PubSub
Live data tables, dashboards Resourceful PubSub
User-specific notifications Either (rooms or model subscribe)

Virtual Requests

The most powerful concept in Sails realtime is virtual requests. When a client sends a message over a WebSocket, the hook converts it into a virtual HTTP request and routes it through the standard Sails router. This means:

  • The same actions handle both HTTP and WebSocket requests
  • Routes, policies, and middleware all apply
  • Sessions are shared between HTTP and WebSocket connections
// This action handles BOTH HTTP GET /api/messages and socket GET /api/messages
// api/controllers/messages/list.js
module.exports = {
  fn: async function () {
    const messages = await Message.find({ room: this.req.param('room') })

    // If this is a socket request, subscribe to future messages
    if (this.req.isSocket) {
      sails.sockets.join(this.req, `room-${this.req.param('room')}`)
    }

    return messages
  }
}

Detecting Socket Requests

Use req.isSocket to check if a request came from a WebSocket:

if (req.isSocket) {
  // This is a WebSocket request
  sails.sockets.join(req, 'some-room')
}

Additional request properties available for socket requests:

req.isSocket // true for WebSocket requests
req.socket // The raw Socket.IO socket object
req.transport // 'socket.io'
req.protocol // 'ws'

Installation

Install sails-hook-sockets:

npm install sails-hook-sockets

The hook auto-registers with Sails — no manual configuration needed for basic usage.

Client Library

The client-side library sails.io.js provides the SDK for connecting to the Sails WebSocket server. Include it in your frontend:

<script src="/dependencies/sails.io.js"></script>

Or in an Inertia.js app, load it in your entry file:

// assets/js/app.js
import '../dependencies/sails.io.js'

Once loaded, io.socket auto-connects and provides methods to make virtual requests:

// These send WebSocket messages that are routed like HTTP requests
io.socket.get('/api/messages', { room: 'general' }, (data, jwr) => {
  console.log(data) // Response body (same as HTTP response)
  console.log(jwr.statusCode) // HTTP status code
})

io.socket.post(
  '/api/messages',
  {
    room: 'general',
    text: 'Hello!'
  },
  (data, jwr) => {
    console.log('Message sent')
  }
)

Session Integration

WebSocket connections share the same session as HTTP requests. When a user logs in via HTTP, their socket connection has access to the same session data:

// The user logged in via HTTP POST /login
// Their socket connection shares the session
module.exports = {
  fn: async function () {
    // req.session is the same whether HTTP or socket
    const userId = this.req.session.userId

    if (this.req.isSocket && userId) {
      // Subscribe this user's socket to their notification room
      sails.sockets.join(this.req, `user-${userId}`)
    }
  }
}

Session sharing works because sockets send the session cookie during the handshake, and Sails loads the same session store entry.

How It Works Under the Hood

Browser                          Sails Server
───────                          ────────────
1. Page loads sails.io.js
2. io.socket auto-connects  ──→  Socket.IO handshake
                                  ↓
                                  Session loaded from cookie
                                  beforeConnect() runs (if configured)
                                  Socket connection accepted
                                  ↓
3. io.socket.get('/api/x') ──→  Virtual request created
                                  ↓
                                  Routed through Sails router
                                  Policies applied
                                  Action executed
                                  ↓
4. Callback fired          ←──  Response sent via socket callback

5. Server pushes update    ←──  sails.sockets.broadcast('room', data)
   io.socket.on('event')

Quick Example: Live Counter

A minimal example showing both tiers in action:

// api/controllers/counter/subscribe.js
module.exports = {
  fn: async function () {
    if (!this.req.isSocket) {
      return this.res.badRequest(
        'This endpoint requires a WebSocket connection'
      )
    }

    sails.sockets.join(this.req, 'counter-watchers')
    return { count: await Visit.count() }
  }
}

// api/controllers/counter/increment.js
module.exports = {
  fn: async function () {
    await Visit.create({ timestamp: Date.now() })
    const count = await Visit.count()

    // Push the new count to all watchers
    sails.sockets.broadcast('counter-watchers', 'counterUpdated', { count })

    return { count }
  }
}

Client side:

// Subscribe to counter updates
io.socket.get('/api/counter/subscribe', (data) => {
  console.log('Initial count:', data.count)
})

// Listen for updates
io.socket.on('counterUpdated', (data) => {
  console.log('New count:', data.count)
})

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