When you want live features — chat, notifications, a shared game board — you have two Node.js options: the low-level ws library, or Socket.IO, which builds named events, rooms, automatic reconnection, and fallbacks on top. This guide gets a Socket.IO round-trip running on your one Falix port and is honest about when the extra layer earns its keep.
| At a glance | |
|---|---|
| You need | A server running the Node.js application |
| Background | Your first web app and, ideally, Real-time apps with WebSockets |
| Plan | Any plan |
| Time | Twenty-five minutes |
Socket.IO vs plain WebSockets
Socket.IO uses WebSockets underneath, then adds conveniences you'd otherwise build yourself. It is not interoperable with a raw WebSocket client — both ends must speak Socket.IO.
| Socket.IO | Plain ws |
|
|---|---|---|
| Named events | Built in (socket.on('chat', ...)) |
You invent a message format |
| Rooms / broadcasting | Built in (io.to('room').emit(...)) |
You track clients yourself |
| Auto-reconnect | Built in, with backoff | You write it (the ws guide shows how) |
| Fallback when WS is blocked | Falls back to HTTP long-polling | None — WebSocket or nothing |
| Client | Needs the Socket.IO client library | Native browser WebSocket |
| Overhead | A bit more per message | Minimal |
Pick Socket.IO when you want rooms, reconnection, and resilience without writing them — most chat and collaboration apps. Pick plain ws when you want the lightest possible connection, full control of the wire format, or a standard WebSocket that any client can speak. Both serve HTTP and real-time traffic from the same single port, which is exactly what a Falix app server gives you.
The server
Install socket.io from the Packages page (search socket.io, install, restart). Socket.IO attaches to an ordinary HTTP server — the same one that binds your public port:
const express = require('express');
const http = require('node:http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server); // rides on the HTTP server — one port
app.get('/', (req, res) => res.send('Socket.IO server is up.'));
io.on('connection', (socket) => {
console.log('client connected:', socket.id);
// A named event. The client emits 'chat message'; we rebroadcast it.
socket.on('chat message', (msg) => {
io.emit('chat message', msg); // to everyone, including the sender
});
socket.on('disconnect', () => console.log('client left:', socket.id));
});
const PORT = process.env.SERVER_PORT || 8080;
server.listen(PORT, '0.0.0.0', () => console.log(`Listening on port ${PORT}`));
The shape mirrors the WebSockets guide: new Server(server) hands Socket.IO the HTTP server that's already listening, so server.listen (reading SERVER_PORT, binding 0.0.0.0) serves both your web page and the real-time channel. The difference is what you get for free — named events ('chat message' instead of parsing raw strings) and io.emit to broadcast without tracking clients by hand.
The client
Socket.IO needs its own client library (a raw browser WebSocket won't connect). In a browser, load it and connect to your public address:
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io(); // connects back to the server that served this page
socket.on('connect', () => socket.emit('chat message', 'hello!'));
socket.on('chat message', (msg) => console.log('received:', msg));
</script>
Socket.IO serves that client script from /socket.io/socket.io.js automatically. Calling io() with no argument connects to the same origin — no address to hard-code when the page and server are the same app.
Verify it works
The quickest proof is a Node client that connects, sends, and receives. Install socket.io-client, then:
const { io } = require('socket.io-client');
const socket = io('http://127.0.0.1:' + (process.env.SERVER_PORT || 8080));
socket.on('connect', () => {
console.log('connected as', socket.id);
socket.emit('chat message', 'hello from the client');
});
socket.on('chat message', (msg) => {
console.log('received broadcast ->', msg);
process.exit(0);
});
Run it against the server and you'll see the full loop — the client connects, emits chat message, the server's io.emit broadcasts it, and the client logs received broadcast -> hello from the client. That round-trip is your real-time channel working.
Rooms — the reason to reach for Socket.IO
Rooms are why most people choose Socket.IO. A socket can join named rooms, and you can broadcast to just one:
io.on('connection', (socket) => {
socket.on('join', (room) => socket.join(room));
socket.on('message', ({ room, text }) => {
io.to(room).emit('message', text); // only clients in that room
});
});
That's per-channel chat, per-game lobbies, or per-document collaboration with almost no bookkeeping — the exact thing you'd hand-roll over plain ws.
Reconnection and reality
Socket.IO's client reconnects automatically with backoff, so a phone changing networks or a brief hiccup heals itself — no code from you. Two honest caveats for Falix:
⚠️ Heads up: Behind the Reverse Proxy for HTTPS, connect with the secure endpoint and let Socket.IO negotiate — it will use
wss://and fall back to secure long-polling if needed (Domains and HTTPS). And every open connection costs memory: broadcasting to thousands multiplies the work, and the free plan's 2.5 GB shared RAM fills faster than you'd expect (Out of memory).
Scaling Socket.IO across multiple processes needs a Redis adapter so broadcasts reach clients on every process — relevant only once you outgrow one process; the normal single-process case here needs nothing extra. Everything beyond the Falix layer is standard Socket.IO, documented in full at socket.io.
Troubleshooting
- Client never connects — you're using a raw browser
WebSocketinstead of the Socket.IO client, or the versions differ. Both ends must be Socket.IO, and major versions must match. - Connects then drops immediately — the HTTP server bound
localhostinstead of0.0.0.0, so nothing outside the container reaches it. Bind0.0.0.0and readSERVER_PORT. - Works locally, not in production — a hard-coded port. Use
process.env.SERVER_PORT. - Secure page refuses to connect — an HTTPS page can't use an insecure endpoint. Serve through the Reverse Proxy so Socket.IO uses
wss://.
Next steps
- Real-time apps with WebSockets — the lower-level alternative
- Socket.io chat room — a complete build
- Redis caching — the adapter for scaling out
- Out of memory — when connections pile up