CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rpc-websockets

JSON-RPC 2.0 implementation over WebSockets for Node.js and browser with bidirectional communication support

Pending
Overview
Eval results
Files

server.mddocs/

WebSocket Server

JSON-RPC 2.0 server implementation with method registration, event management, namespace support, and authentication middleware for building real-time WebSocket applications.

Capabilities

Server Constructor

Creates a WebSocket RPC server with configurable options and data serialization.

/**
 * WebSocket RPC server implementation
 * Extends EventEmitter for connection and error handling
 */
class Server extends EventEmitter {
  /** WebSocket Server instance for direct access to underlying server */
  wss: InstanceType<typeof WebSocketServer>;
  
  /**
   * Instantiate a Server class
   * @param options - WebSocket server constructor parameters
   * @param dataPack - custom data pack for encoding/decoding messages
   * @returns new Server instance
   */
  constructor(
    options: NodeWebSocket.ServerOptions,
    dataPack?: DataPack<object, string>
  );
}

Usage Examples:

import { Server } from "rpc-websockets";

// Basic server on specific port
const server = new Server({ port: 8080 });

// Server with host and port
const server = new Server({ 
  host: "localhost", 
  port: 8080 
});

// Server with custom options
const server = new Server({
  port: 8080,
  perMessageDeflate: false,
  maxPayload: 16 * 1024 * 1024 // 16MB
});

// Server with external HTTP server
import { createServer } from "http";
const httpServer = createServer();
const server = new Server({ server: httpServer });
httpServer.listen(8080);

Method Registration

Register RPC methods that clients can call, with support for protection levels.

/**
 * Registers an RPC method
 * @param name - method name that clients will call
 * @param fn - handler function receiving parameters and socket ID
 * @param ns - namespace identifier (default: "/")
 * @returns IMethod object with protection control methods
 * @throws TypeError for invalid parameters
 */
register(
  name: string,
  fn: (params: IRPCMethodParams, socket_id: string) => any,
  ns?: string
): IMethod;

interface IMethod {
  /** Mark method as protected (requires authentication) */
  protected(): void;
  /** Mark method as public (default, no authentication required) */
  public(): void;
}

interface IRPCMethodParams {
  [x: string]: any;
}

Usage Examples:

// Simple public method
server.register("sum", (params) => {
  return params[0] + params[1];
});

// Method with socket access
server.register("getUserInfo", (params, socket_id) => {
  console.log(`Request from socket: ${socket_id}`);
  return { id: params.id, name: "User Name" };
});

// Protected method requiring authentication
server.register("getSecretData", () => {
  return { secret: "confidential information" };
}).protected();

// Method in custom namespace
server.register("adminCommand", (params) => {
  return { status: "executed", command: params.command };
}, "/admin");

// Async method
server.register("fetchData", async (params) => {
  const response = await fetch(params.url);
  return await response.json();
});

Authentication Setup

Configure authentication middleware for protecting methods and events.

/**
 * Sets an auth method for login functionality
 * @param fn - authentication function returning Promise<boolean>
 * @param ns - namespace identifier (default: "/")
 */
setAuth(
  fn: (params: IRPCMethodParams, socket_id: string) => Promise<boolean>,
  ns?: string
): void;

Usage Examples:

// Simple username/password authentication
server.setAuth(async (params, socket_id) => {
  const { username, password } = params;
  
  // Validate credentials (example)
  if (username === "admin" && password === "secret") {
    console.log(`User ${username} authenticated from socket ${socket_id}`);
    return true;
  }
  
  return false;
});

// Database-based authentication
server.setAuth(async (params, socket_id) => {
  try {
    const user = await database.validateUser(params.token);
    if (user) {
      console.log(`User ${user.id} authenticated`);
      return true;
    }
  } catch (error) {
    console.error("Auth error:", error);
  }
  return false;
});

// Namespace-specific authentication
server.setAuth(async (params) => {
  return params.adminKey === "admin-secret";
}, "/admin");

Event Management

Create and manage events that can be emitted to subscribed clients.

/**
 * Creates a new event that can be emitted to clients
 * @param name - event name that clients can subscribe to
 * @param ns - namespace identifier (default: "/")
 * @returns IEvent object with protection control methods
 * @throws TypeError if event already exists
 */
event(name: string, ns?: string): IEvent;

/**
 * Lists all created events in a given namespace
 * @param ns - namespace identifier (default: "/")
 * @returns array of event names
 */
eventList(ns?: string): string[];

interface IEvent {
  /** Mark event as protected (requires authentication to subscribe) */
  protected(): void;
  /** Mark event as public (default, no authentication required) */
  public(): void;
}

Usage Examples:

// Create public events
server.event("userJoined");
server.event("messagePosted");
server.event("feedUpdated");

// Create protected event
server.event("adminAlert").protected();

// Emit events to subscribers
server.emit("userJoined", { 
  userId: 123, 
  username: "alice", 
  timestamp: Date.now() 
});

server.emit("messagePosted", {
  messageId: 456,
  content: "Hello world!",
  author: "alice"
});

// List available events
const events = server.eventList();
console.log("Available events:", events);

// Events in specific namespace
server.event("specialEvent", "/admin");
const adminEvents = server.eventList("/admin");

Namespace Management

Organize methods and events into separate namespaces for multi-tenant applications.

/**
 * Returns a requested namespace object with convenience methods
 * @param name - namespace identifier
 * @returns namespace object with scoped methods
 */
of(name: string): {
  /** Register method in this namespace */
  register(fn_name: string, fn: (params: IRPCMethodParams) => any): IMethod;
  /** Create event in this namespace */
  event(ev_name: string): IEvent;
  /** Get list of events in this namespace */
  readonly eventList: string[];
  /** Emit event to this namespace */
  emit(event: string, ...params: Array<string>): void;
  /** Get namespace name */
  readonly name: string;
  /** Get connected clients in this namespace */
  connected(): {};
  /** Get namespace client data */
  clients(): {
    rpc_methods: IRPCMethod;
    clients: Map<string, IClientWebSocket>;
    events: INamespaceEvent;
  };
};

/**
 * Removes a namespace and closes all connections
 * @param ns - namespace identifier
 */
closeNamespace(ns: string): void;

Usage Examples:

// Work with admin namespace
const adminNS = server.of("/admin");

// Register methods in namespace
adminNS.register("restart", () => {
  return { status: "restarting" };
});

adminNS.register("getStats", () => {
  return { users: 100, uptime: "2 days" };
});

// Create events in namespace
adminNS.event("systemAlert");

// Emit to namespace
adminNS.emit("systemAlert", "High memory usage detected");

// Get namespace info
console.log("Admin namespace events:", adminNS.eventList);
console.log("Connected admin clients:", Object.keys(adminNS.connected()));

// Close namespace when done
server.closeNamespace("/admin");

Error Handling

Create JSON-RPC 2.0 compliant error responses and handle server errors.

/**
 * Creates a JSON-RPC 2.0 compliant error
 * @param code - indicates the error type that occurred
 * @param message - provides a short description of the error
 * @param data - details containing additional information about the error
 * @returns JSON-RPC error object
 */
createError(code: number, message: string, data: string | object): {
  code: number;
  message: string;
  data: string | object;
};

Standard JSON-RPC Error Codes:

  • -32700: Parse error
  • -32600: Invalid Request
  • -32601: Method not found
  • -32602: Invalid params
  • -32603: Internal error
  • -32000: Event not provided
  • -32604: Params not found
  • -32605: Method forbidden
  • -32606: Event forbidden

Usage Examples:

// Method that returns custom error
server.register("validateData", (params) => {
  if (!params.email) {
    throw server.createError(-32602, "Invalid params", "Email is required");
  }
  
  if (!params.email.includes("@")) {
    throw server.createError(-32000, "Validation failed", "Invalid email format");
  }
  
  return { valid: true };
});

// Handle server errors
server.on("error", (error) => {
  console.error("Server error:", error);
});

// Handle socket errors
server.on("socket-error", (socket, error) => {
  console.error(`Socket ${socket._id} error:`, error);
});

Server Lifecycle

Control server startup, shutdown, and connection handling.

/**
 * Closes the server and terminates all clients
 * @returns Promise resolving when server is closed
 */
close(): Promise<void>;

Usage Examples:

// Server lifecycle events
server.on("listening", () => {
  console.log("Server listening on port 8080");
});

server.on("connection", (socket, request) => {
  console.log(`New connection: ${socket._id}`);
  console.log(`URL: ${request.url}`);
});

server.on("disconnection", (socket) => {
  console.log(`Client disconnected: ${socket._id}`);
});

// Graceful shutdown
process.on("SIGTERM", async () => {
  console.log("Shutting down server...");
  await server.close();
  process.exit(0);
});

Server Interface Types

interface IRPCMethod {
  [x: string]: {
    fn: (params: IRPCMethodParams, socket_id: string) => any;
    protected: boolean;
  };
}

interface INamespaceEvent {
  [x: string]: {
    sockets: Array<string>;
    protected: boolean;
  };
}

interface IClientWebSocket extends NodeWebSocket {
  _id: string;
  _authenticated: boolean;
}

interface IRPCError {
  code: number;
  message: string;
  data?: string;
}

Server Events

The server emits the following events during its lifecycle:

  • listening: Fired when server starts listening for connections
  • connection: Fired when a new client connects (socket, request)
  • disconnection: Fired when a client disconnects (socket)
  • error: Fired when server errors occur (error)
  • socket-error: Fired when individual socket errors occur (socket, error)
  • close: Fired when server is closed

Install with Tessl CLI

npx tessl i tessl/npm-rpc-websockets

docs

client.md

index.md

server.md

tile.json