Skip to content

Nehonix-Team/Sockular

Repository files navigation

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

Sockular is designed to be used with xfpm, an ultra-fast and high-performance package manager compared to npm.

To get started:

  1. Follow the installation guide for xypriss-cli.
  2. Once xypriss-cli is installed, you will have access to xfpm.
  3. Install Sockular:
xfpm install sockular # xfpm i sockular

CLI 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-app

Generate components

# Generate a service
npx sockular g service chat

# Generate a middleware
npx sockular g middleware auth

Quick 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 application
  • with-auth/ - Authentication and authorization
  • with-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

  • CLI tools for scaffolding
  • Built-in rate limiting
  • Auto-discovery of services
  • Unified Client API (Decorative & Fluent)
  • Plugin system
  • Redis adapter for horizontal scaling
  • Prometheus metrics integration
  • WebSocket native support
  • Advanced validation with Zod

By the Nehonix Team

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors