HTTP is request-and-response. A WebSocket stays open, so the server and the browser can push messages both ways in real time — chat, game state, live dashboards. A Falix app server gives you exactly one public port, and the neat part is that a single port can serve your web page and your WebSocket at once. Here's the pattern.
| At a glance | |
|---|---|
| You need | A server running the Node.js application |
| Background | Node.js on Falix and the SERVER_PORT / 0.0.0.0 rule from Your first web app |
| Time | Twenty minutes |
One port, two protocols
A Falix app server has a single public port — the SERVER_PORT value the panel injects. That sounds like a problem for real-time apps until you notice that a WebSocket server can ride on top of an ordinary HTTP server.
🎯 Good to know: Attach the
wslibrary'sWebSocketServerto the samehttp.Serverthat's already listening, and the one port answers both normal web requests and WebSocket upgrades.
Install ws from the Packages page (search ws, install, restart), then write index.js:
const http = require('node:http');
const { WebSocketServer, WebSocket } = require('ws');
// An ordinary HTTP server — this is what binds the public port.
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('HTTP is alive. Open a WebSocket to this same address.');
});
// The WebSocket server rides on the HTTP server — same port.
const wss = new WebSocketServer({ server });
wss.on('connection', (socket) => {
console.log('Client connected');
socket.on('message', (data) => {
const text = data.toString();
// Broadcast to every client that's still open.
for (const client of wss.clients) {
if (client.readyState === WebSocket.OPEN) {
client.send(text);
}
}
});
socket.on('close', () => console.log('Client disconnected'));
});
const PORT = process.env.SERVER_PORT || 8080;
server.listen(PORT, '0.0.0.0', () => {
console.log(`Listening on port ${PORT}`);
});
The whole Falix-shaped insight is new WebSocketServer({ server }): you don't open a second listener, you hand it the HTTP server you already have. server.listen reads SERVER_PORT and binds 0.0.0.0 — the same two rules as any web app — and the WebSocket comes along for free. That Listening line also flips the server to online.
The message loop
wss.on('connection', ...)fires once per client;socketis that one connection.socket.on('message', ...)receives what a client sends.dataarrives as a buffer, so.toString()turns it into text.- The loop over
wss.clientssends the message to everyone — a broadcast. CheckingreadyState === WebSocket.OPENskips clients that are mid-disconnect, which would otherwise throw.
Send to just the one client with socket.send(...); loop over wss.clients to reach all of them. That single choice is the whole difference between an echo and a chat room.
Connecting from a browser
Point a browser WebSocket at your server's public address and port (from the Network page):
const socket = new WebSocket('ws://YOUR_ADDRESS:PORT');
socket.addEventListener('open', () => socket.send('Hello, server!'));
socket.addEventListener('message', (event) => {
console.log('Server says:', event.data);
});
Plain ws:// over the raw address and port works immediately. If you put the site behind the Network page's Reverse Proxy for a real domain with automatic SSL, connect with wss:// instead — the secure WebSocket the proxy expects (Domains and HTTPS).
When a connection drops
A WebSocket is meant to stay open, but no long-lived connection lasts forever — a phone switching networks, a laptop going to sleep, a brief hiccup, or a proxy retiring an idle connection can all close it. That's normal, and every real-time app handles it the same way: reconnect from the client. When the socket fires close, wait a moment and open a fresh one:
function connect() {
const socket = new WebSocket('ws://YOUR_ADDRESS:PORT');
socket.addEventListener('message', (event) => {
console.log('Server says:', event.data);
});
socket.addEventListener('close', () => {
setTimeout(connect, 1000); // try again in a second
});
}
connect();
A light heartbeat helps too: have the client send a tiny message every so often so an idle connection doesn't look dead to anything sitting in between. Don't hard-code a guess about how long a connection "should" survive — assume it can close at any time and reconnect cleanly when it does.
What it's good for
Anything where waiting for a page refresh would feel wrong: live chat, multiplayer game state, collaborative editors, and dashboards that stream metrics as they change. The server above is already a working broadcast hub — build any of those on top of the message loop.
Troubleshooting
- Connection opens then instantly closes — the HTTP server bound
localhost/127.0.0.1instead of0.0.0.0, so nothing outside the container can reach it. Bind0.0.0.0. - Works on your machine, not in production — a hardcoded port (like
8080or3000). Readprocess.env.SERVER_PORT; that's the only reachable port. - A browser on an HTTPS page refuses to connect — a secure page can't open a plain
ws://socket. Serve it through the Reverse Proxy and connect withwss://. - Memory climbs with many clients — every open socket costs memory, and broadcasting to thousands multiplies the work. Watch it, and see Out of memory; the free plan's 2.5 GB shared RAM fills faster than you'd think.