sockular
v0.2.4
Published
A modular, decorator-based framework for building scalable Socket.IO applications with TypeScript
Maintainers
Readme
Sockular
A modular, decorator-based framework for building scalable Socket.IO applications with TypeScript.
Overview
Sockular transforms the way you build real-time applications by providing a clean, modular architecture that eliminates boilerplate code and promotes best practices. Built on top of Socket.IO, it brings structure and maintainability to WebSocket applications through TypeScript decorators and a powerful middleware system.
Key Features
- Decorator-Based API: Define message handlers and room logic using intuitive TypeScript decorators
- Modular Architecture: Organize your real-time logic into reusable services
- Three-Level Middleware System: Apply middleware at global, service, or method level
- Auto-Discovery: Automatically discover and register services from your codebase
- Unified Client API: Fluent and Decorative APIs for seamless frontend integration
- Declarative Room Management: Manage rooms easily with @JoinRoom, @LeaveRoom, and @EmitToRoom
- Socket.IO Dependency Injection: Inject the Socket.IO server instance directly into your services
- Type-Safe: Full TypeScript support with comprehensive type definitions
- Secure by Default: Global middleware enforcement with explicit opt-out for public endpoints
- Built-in Rate Limiting: Protect your server from spam with configurable built-in rate limiting
- CLI Tools: Scaffolding commands for project initialization and component generation (
scr/sockular)
Installation
npm install sockularCLI Usage
Sockular comes with a powerful CLI to speed up your development. You can use either sockular or the shortcut scr.
Initialize a new project
npx sockular init my-appGenerate components
# Generate a service
npx sockular g service chat
# Generate a middleware
npx sockular g middleware authQuick Start
1. Create a Service
import { Service, OnMessage, OnConnect, OnDisconnect } from 'sockular';
import { Socket } from 'sockular';
@Service('chat')
export class ChatService {
@OnConnect()
async handleConnect(socket: Socket, clientId: string) {
console.log(`Client ${clientId} connected to chat`);
}
@OnMessage('send_message')
async handleSendMessage(socket: Socket, data: any, clientId: string) {
// Handle incoming message
socket.emit('message_received', {
message: data.message,
timestamp: Date.now(),
});
}
@OnDisconnect()
async handleDisconnect(socket: Socket, clientId: string) {
console.log(`Client ${clientId} disconnected`);
}
}2. Initialize the Server
import { SockularServer } from 'sockular';
import { ChatService } from './services/chat.service';
const server = new SockularServer({
port: 3000,
enableLogging: true,
});
// Register services
server.register(ChatService);
// Start the server
await server.start();3. Connect from Client
import io from 'socket.io-client';
const socket = io('http://localhost:3000', {
auth: { token: 'your-auth-token' },
});
socket.emit('message', {
service: 'chat',
type: 'send_message',
data: { message: 'Hello, world!' },
});4. Using the Sockular Client (Recommended)
Sockular provides a dedicated client for a better developer experience:
import { Client } from 'sockular';
@Client.SetUrl('http://localhost:3000')
class ChatClient {
@Client.OnConnect()
onConnect() {
console.log('Connected!');
}
@Client.OnMessage('chat', 'message_received')
onMessage(data: any) {
console.log('Received:', data.message);
}
}
const chat = Client.create(ChatClient);See the Client API Documentation for more details.
Middleware System
Sockular provides a powerful three-level middleware system for authentication, validation, rate limiting, and more.
Defining Middleware
import { Middleware } from 'sockular';
export const authMiddleware: Middleware = async (socket, data, clientId, next) => {
const token = socket.handshake.auth?.token;
if (!token) {
throw new Error('Authentication required');
}
const user = await validateToken(token);
socket.data.user = user;
await next();
};Applying Middleware
// Global middleware (applied to all messages)
server.registerMiddleware('auth', authMiddleware);
server.use('auth');
// Service-level middleware
@Service('chat', {
middleware: ['validation']
})
export class ChatService { }
// Method-level middleware
@OnMessage('admin_action')
@UseMiddleware('adminOnly')
async handleAdminAction(socket: Socket, data: any, clientId: string) { }
// Skip global middleware for public endpoints
@OnMessage('login')
@SkipMiddleware('auth')
async handleLogin(socket: Socket, data: any, clientId: string) { }Decorators
@Service(name, options?)
Marks a class as a Sockular service.
@Service('chat', {
middleware: ['validation'],
rateLimit: { maxRequests: 100, windowMs: 60000 },
})
export class ChatService {}@OnMessage(messageType)
Defines a handler for a specific message type.
@OnMessage('send_message')
async handleSendMessage(socket: Socket, data: any, clientId: string) { }@OnConnect()
Called when a client connects to the service.
@OnConnect()
async handleConnect(socket: Socket, clientId: string) { }@OnDisconnect()
Called when a client disconnects.
@OnDisconnect()
async handleDisconnect(socket: Socket, clientId: string) { }@UseMiddleware(...names)
Applies additional middleware to a specific method.
@UseMiddleware('adminOnly', 'validation')
async handleAdminAction(socket: Socket, data: any, clientId: string) { }@SkipMiddleware(...names)
Excludes global middleware from a specific method.
@SkipMiddleware('auth')
async handlePublicEndpoint(socket: Socket, data: any, clientId: string) { }
### @JoinRoom(room)
Automatically adds the client's socket to a specific room. The `room` parameter can be a string or a function taking `data` as an argument.
```typescript
@OnMessage('join_chat')
@JoinRoom((data) => `room_${data.id}`)
async handleJoin() {}@LeaveRoom(room)
Automatically removes the client's socket from a specific room.
@OnMessage('leave_chat')
@LeaveRoom('global_chat')
async handleLeave() {}@EmitToRoom(room, event)
Broadcasts the returned value of the handler to everyone in the specified room.
@OnMessage('send_message')
@EmitToRoom((data) => `room_${data.id}`, 'new_message')
async handleSend(socket, data) {
return { text: data.message };
}@SocketIO()
Injects the native Socket.IO Server instance into a class property.
@SocketIO()
private io: Server;
## Auto-Discovery
Automatically discover and register services from a directory:
```typescript
await server.discoverServices('./services');Configuration
const server = new SockularServer({
port: 3000,
host: 'localhost',
enableLogging: true,
enableHeartbeat: true,
sessionTimeout: 600000,
maxConnections: 1000,
rateLimit: {
enabled: true,
windowMs: 60000,
maxRequests: 100,
global: true, // Apply to all messages automatically
message: 'Too many requests, please try again later.',
},
});Built-in Rate Limiting
Sockular includes a built-in rate limiting middleware that can be enabled globally or registered for specific use.
const server = new SockularServer({
port: 3000,
rateLimit: {
enabled: true,
windowMs: 5000, // 5 seconds
maxRequests: 5,
global: true,
},
});If global is set to false, you can still apply it to specific services or methods using the name 'rateLimit':
@Service('chat')
@UseMiddleware('rateLimit')
export class ChatService {}Architecture Benefits
Before Sockular
io.on('connection', (socket) => {
socket.on('send_message', async (data) => {
/* ... */
});
socket.on('typing', async (data) => {
/* ... */
});
socket.on('join_room', async (data) => {
/* ... */
});
socket.on('leave_room', async (data) => {
/* ... */
});
socket.on('delete_message', async (data) => {
/* ... */
});
// Hundreds of handlers in one file...
});With Sockular
@Service('chat')
export class ChatService {
@OnMessage('send_message')
async handleSendMessage(socket, data, clientId) {}
@OnMessage('typing')
async handleTyping(socket, data, clientId) {}
@OnMessage('join_room')
async handleJoinRoom(socket, data, clientId) {}
}Clean, modular, and maintainable.
Examples
See the examples/ directory for complete working examples:
basic/- Simple chat applicationwith-auth/- Authentication and authorizationwith-redis/- Horizontal scaling with Redis adapter
Requirements
- Node.js >= 16
- TypeScript >= 4.5
- Socket.IO >= 4.0
Contributing
Contributions are welcome. Please open an issue or submit a pull request.
Support
For questions and support, please open an issue on GitHub.
Roadmap
- [x] CLI tools for scaffolding
- [x] Built-in rate limiting
- [x] Auto-discovery of services
- [x] Unified Client API (Decorative & Fluent)
- [ ] Plugin system
- [ ] Redis adapter for horizontal scaling
- [ ] Prometheus metrics integration
- [ ] WebSocket native support
- [ ] Advanced validation with Zod
Built with ❤️ by the Nehonix Team
