Guides

Configuration

Complete reference for all IOServerOptions fields and LogLevel values.

Configuration

IOServerOptions

All fields are optional. Pass them as a plain object to the IOServer constructor.

import { IOServer, IOServerOptions } from "ioserver";

const server = new IOServer({
  host: "0.0.0.0",
  port: 3000,
  verbose: "INFORMATION",
  routes: "./routes",
  cors: {
    origin: ["https://app.example.com"],
    methods: ["GET", "POST"],
  },
  mode: ["websocket", "polling"],
  cookie: false,
  rootDir: "./public",
  spaFallback: true,
});
FieldTypeDefaultDescription
hoststring"localhost"Hostname or IP to bind
portnumber8080TCP port (1–65535)
verboseLogLevel"ERROR"Minimum log level to output
routesstring"./routes"Path to JSON route files directory
corsobjectlocalhost originsPassed to @fastify/cors
modeTransportMode | TransportMode[]["websocket", "polling"]Socket.IO transports
cookiebooleanfalseEnable Socket.IO cookies
rootDirstringundefinedAbsolute path to static files directory (e.g. built SPA). Silently disabled if path does not exist
spaFallbackbooleantrueWhen rootDir is active, serve index.html for unknown routes (enables client-side routing)

verbose — LogLevel

Log messages are only printed when their level is at or below the configured verbose level.

LevelNumberWhen to use
"EMERGENCY"0System unusable
"ALERT"1Immediate action required
"CRITICAL"2Critical conditions
"ERROR"3Error conditions (default)
"WARNING"4Warning conditions
"NOTIFICATION"5Normal but significant events
"INFORMATION"6Informational messages
"DEBUG"7Verbose debug output
// In your components, log with numeric level:
this.appHandle.log(3, "Critical error occurred");    // always shown (≤ ERROR)
this.appHandle.log(6, "User connected");             // shown if verbose ≥ INFORMATION
this.appHandle.log(7, "Received raw payload: ...");  // shown only in DEBUG

Levels 0–4 are printed to stderr (console.error). Levels 5–7 go to stdout (console.log).

cors

Passed directly to @fastify/cors. Credentials default to false and optionsSuccessStatus defaults to 204.

cors: {
  origin: ["https://app.example.com", "https://admin.example.com"],
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true,
}

mode — TransportMode

Controls Socket.IO transport negotiation:

mode: "websocket"                    // WebSocket only
mode: "polling"                      // Long polling only
mode: ["websocket", "polling"]       // Both (default, clients negotiate)

routes

Directory where controller JSON route files are stored. The path can be relative (resolved from process.cwd()) or absolute. IOServer falls back to ./routes if the provided path does not exist.

routes: "./routes"                         // relative
routes: path.join(__dirname, "../routes")  // absolute
routes: "./examples/chat-app/routes"       // example app

rootDir and spaFallback

Serve a frontend SPA alongside your API:

const server = new IOServer({
  port: 3000,
  rootDir: path.join(__dirname, "../frontend/dist"), // must exist
  spaFallback: true,    // default — unknown GET requests return index.html
});

If rootDir does not exist at startup, a WARNING is logged and static serving is silently disabled — the server starts normally. This makes it safe to always set rootDir in code, even during development before the frontend is built.

API routes registered via controllers always take priority over static files.

Environment-based configuration

const server = new IOServer({
  host: process.env.HOST ?? "0.0.0.0",
  port: parseInt(process.env.PORT ?? "3000", 10),
  verbose: (process.env.LOG_LEVEL ?? "ERROR") as LogLevel,
  cors: {
    origin: process.env.ALLOWED_ORIGINS?.split(",") ?? ["http://localhost:3000"],
  },
});

Runtime introspection

After construction (before or after start()):

server.getHost();                         // string
server.getPort();                         // number
server.getApp();                          // FastifyInstance
server.getService("chat");                // service instance or undefined
server.isRegistered("service", "chat");   // boolean
server.isRegistered("manager", "db");     // boolean
server.log(6, "message");                 // manual log
Copyright © 2026