Api

Base Classes

Reference for all five abstract base classes — BaseService, BaseController, BaseManager, BaseWatcher, BaseMiddleware.

Base Classes

All base classes are exported from ioserver and receive an AppHandle in their constructor.

Import

import {
  BaseService,
  BaseController,
  BaseManager,
  BaseWatcher,
  BaseMiddleware,
} from "ioserver";

BaseService

abstract class BaseService {
  protected appHandle: AppHandle;
  constructor(appHandle: AppHandle);
}

Extend this class to create a WebSocket service. Every public method whose name does not start with _ and is not constructor is automatically registered as a Socket.IO event handler on the service's namespace.

Method signature (required):

async methodName(socket: any, data: any, callback?: Function): Promise<void>

Example:

class PingService extends BaseService {
  async ping(socket: any, _data: any, callback?: Function): Promise<void> {
    if (callback) callback({ pong: true, ts: Date.now() });
    else socket.emit("pong", { ts: Date.now() });
  }

  // Not exposed as a WS event
  private _validate(data: any): boolean {
    return data !== null && data !== undefined;
  }
}

See Services for full documentation.


BaseController

abstract class BaseController {
  protected appHandle: AppHandle;
  constructor(appHandle: AppHandle);
}

Extend this class to create an HTTP controller. Methods are mapped to routes via a JSON route file — they are not auto-discovered.

Method signature (Fastify handlers):

async methodName(request: FastifyRequest, reply: FastifyReply): Promise<void>

Example:

class HealthController extends BaseController {
  async getHealth(request: any, reply: any): Promise<void> {
    reply.send({ status: "ok", ts: new Date().toISOString() });
  }
}

See Controllers for full documentation.


BaseManager

abstract class BaseManager {
  protected appHandle: AppHandle;
  constructor(appHandle: AppHandle);

  // Optional lifecycle hook — called automatically after instantiation
  async start?(): Promise<void>;
}

Extend this class to create a shared singleton. The optional start() method is called automatically (non-blocking) after addManager().

Example:

class TokenManager extends BaseManager {
  private readonly secret: string;

  constructor(appHandle: AppHandle) {
    super(appHandle);
    this.secret = process.env.JWT_SECRET ?? "change-me";
  }

  async start(): Promise<void> {
    this.appHandle.log(6, "TokenManager initialized");
  }

  sign(payload: object): string {
    // sign JWT...
    return "token";
  }

  verify(token: string): object | null {
    // verify JWT...
    return null;
  }
}

See Managers for full documentation.


BaseWatcher

abstract class BaseWatcher {
  protected appHandle: AppHandle;
  constructor(appHandle: AppHandle);

  abstract watch(): Promise<void>;  // Called at server.start()
  abstract stop(): void;            // Called at server.stop()
}

Both abstract methods must be implemented. watch() is called at server.start() (non-blocking). stop() is called at server.stop().

Example:

class HeartbeatWatcher extends BaseWatcher {
  private intervalId: ReturnType<typeof setInterval> | null = null;

  async watch(): Promise<void> {
    this.intervalId = setInterval(() => {
      this.appHandle.log(7, `Heartbeat — heap: ${Math.round(process.memoryUsage().heapUsed / 1e6)}MB`);
    }, 10_000);
  }

  stop(): void {
    if (this.intervalId !== null) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }
}

See Watchers for full documentation.


BaseMiddleware

abstract class BaseMiddleware {
  abstract handle(appHandle: AppHandle): (req: any, reply: any, done: any) => void;
}

Extend this class to create HTTP and/or WebSocket middleware. BaseMiddleware is the only base class that does not receive appHandle in its constructor — instead, appHandle is passed to handle() each time the middleware is instantiated.

BaseMiddleware is instantiated fresh each time a middleware is applied to a route or namespace (unlike managers which are singletons).

HTTP middleware example:

class LoggingMiddleware extends BaseMiddleware {
  handle(appHandle: AppHandle) {
    return (req: any, _reply: any, done: any) => {
      appHandle.log(6, `${req.method} ${req.url}`);
      done();
    };
  }
}

WebSocket middleware example:

class WsAuthMiddleware extends BaseMiddleware {
  handle(appHandle: AppHandle) {
    return (socket: any, next: any) => {
      const token = socket.handshake.auth?.token;
      if (!token) return next(new Error("Token required"));
      const userId = appHandle.sessionManager?.validate(token);
      if (!userId) return next(new Error("Invalid token"));
      socket.userId = userId;
      next();
    };
  }
}

See Middlewares for full documentation.

Copyright © 2026