RedWeb
RedWeb is a flexible Node.js framework built on top of Express.js and WebSocket. It enables quick setup of HTTP(S) and WebSocket servers with a modular route and handler system.
π¦ Installation
npm install redweb
π Quick Start
const { HttpServer, SocketServer } = require('redweb');
new HttpServer();
new SocketServer();
π HTTP Server Example (HTMX Support)
const { HttpServer, METHODS } = require('redweb');
new HttpServer({
port: 3000,
publicPaths: ['./public'],
enableHtmxRendering: true,
services: [
{
serviceName: '/submit',
method: METHODS.POST,
function: (req, res) => {
if (!req.body.name) return res.status(400).json({ error: 'Missing name' });
res.status(200).json({ message: `Thanks, ${req.body.name}!` });
}
}
]
});
.htmx
files under public/
will render server-side. Example:
<@>
<h1>Hello, {{name}}!</h1>
<@/>
π WebSocket Broadcast Chat (π₯ Instant Testing)
1. ChatHandler.js
const { BaseHandler } = require('redweb');
class ChatHandler extends BaseHandler {
constructor() {
super('chat');
}
onMessage(socket, message) {
const text = message.text;
socket.broadcast({ type: 'chat', text });
}
}
module.exports = ChatHandler;
2. ChatRoute.js
const { SocketRoute } = require('redweb');
const ChatHandler = require('./ChatHandler');
class ChatRoute extends SocketRoute {
constructor() {
super({
path: '/chat',
handlers: [ChatHandler],
allowDuplicateConnections: true
});
}
}
module.exports = ChatRoute;
3. server.js
const { SocketServer } = require('redweb');
const ChatRoute = require('./ChatRoute');
new SocketServer({
port: 3000,
routes: [ChatRoute]
});
4. client.html
<!DOCTYPE html>
<html>
<body>
<h1>Broadcast Chat</h1>
<input id="msg" placeholder="Type message..." />
<button onclick="send()">Send</button>
<pre id="log"></pre>
<script>
const log = document.getElementById('log');
const ws = new WebSocket('ws://localhost:3000/chat');
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
log.textContent += `\n${msg.text}`;
};
function send() {
const text = document.getElementById('msg').value;
ws.send(JSON.stringify({ type: 'chat', text }));
}
</script>
</body>
</html>
Open multiple tabs to test.
π§© Socket Architecture
SocketRoute
Defines a WebSocket path, handlers, and optional route-scoped services:
new SocketRoute({
path: '/game',
handlers: [ChatHandler, MoveHandler],
services: [MatchService],
allowDuplicateConnections: true
});
BaseHandler
Handlers are message-type keyed classes:
class MoveHandler extends BaseHandler {
constructor() {
super('move');
}
onMessage(socket, message) {
}
}
SocketService
(NEW)
Socket services run alongside handlers on a route. Use for timers, logic, cleanup.
class MatchService extends SocketService {
constructor() {
super('match', 1000);
}
onInit(route) {
route.registry.on('maxPlayersReached', () => this.startMatch());
}
onTick() {
}
onShutdown() {
}
}
π¦ SocketRegistry
(NEW) β Event-Driven Socket Object Store
SocketRegistry
is a lightweight, extendable class for managing WebSocket-connected clients (or any socket-bound object). It provides add/remove/get/broadcast utilities with full EventEmitter
support.
Useful for managing players, NPCs, chat members, rooms, etc.
π§ Basic Usage
const { SocketRegistry } = require('redweb');
class Player {
constructor(socket, id) {
this.socket = socket;
this.id = id;
}
send(type, payload) {
this.socket.send(JSON.stringify({ type, ...payload }));
}
getSanitized() {
return { id: this.id };
}
}
π Extending SocketRegistry
to Create a Player Registry
class PlayerRegistry extends SocketRegistry {
create(socket, id) {
return new Player(socket, id);
}
addPlayer(socket, id) {
const player = this.create(socket, id);
const success = this.add(player);
if (success) this.emit('playerJoined', player);
return success;
}
removePlayer(id) {
const success = this.remove(id);
if (success) this.emit('playerLeft', id);
return success;
}
broadcastToAll(message) {
this.items.forEach(player => player.send(message.type, message));
}
}
π£ Built-in Events
You can listen to events:
const registry = new PlayerRegistry();
registry.on('playerJoined', player => {
console.log('New player:', player.id);
});
registry.on('playerLeft', id => {
console.log('Player left:', id);
});
π Built-in Methods
add(player)
remove(id)
getById(id)
getBySocket(socket)
all()
count()
broadcast(message, excludeSocket?)
getSanitizedList()
π§ Configuration
HTTP / HTTPS Options
port | number | 80 | Port to listen on |
bind | string | '0.0.0.0' | Bind address |
publicPaths | string[] | ['./public'] | Serve static and .htmx files |
services | object[] | [] | REST endpoints |
enableHtmxRendering | boolean | false | Enables .htmx file rendering |
ssl | object | undefined | Used in HttpsServer |
WebSocket Server Options
port | number | 3000 | WebSocket port |
routes | SocketRoute[] | [] | List of custom route classes |
Β
Changelog
Hereβs the updated CHANGELOG.md
entry for RedWeb v0.7.1, written professionally and focused only on the framework-level additions:
π¦ RedWeb v0.7.1 β Socket Services & Registries
β¨ Added
-
SocketService
: A new class for running autonomous, lifecycle-aware logic alongside a SocketRoute
. Ideal for game loops, timers, state machines, or server-side AI.
- Hooks:
onInit(route)
, onTick()
, onShutdown()
- Optional
tickRateMs
support for periodic execution
-
SocketRegistry
: A generic, event-driven registry for managing WebSocket-bound entities
- Includes
.add()
, .remove()
, .getById()
, .broadcast()
- Fully compatible with custom socket wrappers and
EventEmitter
0.7.0 Update Highlights
- allowDuplicateConnections for multi-tab testing
- Robust message validation
- socket.broadcast() now excludes sender
- Better error handling
πͺͺ License
MIT