Only show guides for your app
Socket.IO gives you rooms, events, auto-reconnect, and fallbacks on top of WebSockets — the higher-level option for chat and live features. A verified server-plus-client round-trip, and an honest comparison with plain ws.
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 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.
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.
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.
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 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.
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.
WebSocket instead of the Socket.IO client, or the versions differ. Both ends must be Socket.IO, and major versions must match.localhost instead of 0.0.0.0, so nothing outside the container reaches it. Bind 0.0.0.0 and read SERVER_PORT.process.env.SERVER_PORT.wss://.