Api
Error Handling
IOServerError — structured errors with HTTP status codes for services and controllers.
Error Handling
IOServerError
IOServerError is the standard error class for IOServer applications. It extends the native Error class with an HTTP status code.
import { IOServerError } from "ioserver";
Constructor
new IOServerError(message: string, statusCode: number = 500): IOServerError
| Parameter | Type | Default | Description |
|---|---|---|---|
message | string | — | Human-readable error message |
statusCode | number | 500 | HTTP status code |
Properties
class IOServerError extends Error {
public statusCode: number;
public name: string; // Always "IOServerError"
}
Examples
throw new IOServerError("User not found", 404);
throw new IOServerError("Invalid email format", 400);
throw new IOServerError("Admin access required", 403);
throw new IOServerError("Rate limit exceeded", 429);
throw new IOServerError("Database unavailable", 503);
throw new IOServerError("Something went wrong"); // statusCode defaults to 500
Error handling in Controllers (HTTP)
Fastify's error handler (configured by IOServer) serialises IOServerError to a structured JSON response:
{
"statusCode": 404,
"error": "IOServerError",
"message": "User not found"
}
Other errors (with .status property) or plain Error instances are handled similarly but use 500 as the default status code.
Pattern:
export class UserController extends BaseController {
async getUser(request: any, reply: any): Promise<void> {
const { id } = request.params as { id: string };
if (!id?.match(/^[0-9a-f-]{36}$/)) {
throw new IOServerError("Invalid user ID format", 400);
}
const user = await this.appHandle.userManager.findById(id);
if (!user) throw new IOServerError("User not found", 404);
reply.send(user);
}
}
Error handling in Services (WebSocket)
IOServer catches any throw from a service method and returns a structured error to the client:
- If a
callbackwas provided:callback({ status: "error", type, message, statusCode }) - Otherwise:
socket.emit("error", { status: "error", type, message, statusCode })
Error payload:
{
status: "error",
type: "IOServerError", // error.constructor.name
message: "Username is required",
statusCode: 400,
}
Pattern:
export class AuthService extends BaseService {
async login(socket: any, data: { username: string }, callback?: Function): Promise<void> {
if (!data?.username?.trim()) {
throw new IOServerError("Username is required", 400);
}
if (this.usernames.has(data.username.toLowerCase())) {
throw new IOServerError("Username already taken", 409);
}
// ...
}
}
Client-side (browser):
socket.emit("login", { username: "" }, (response) => {
if (response.status === "error") {
console.error(`${response.statusCode}: ${response.message}`);
// "400: Username is required"
}
});
// Without callback — listen on "error"
socket.on("error", (err) => {
console.error(err.message); // "Username is required"
});
Checking instanceof
The prototype chain is correctly fixed, so instanceof checks work:
try {
someOperation();
} catch (err) {
if (err instanceof IOServerError) {
console.log(err.statusCode); // 404
}
}
Framework-level errors
IOServer itself throws IOServerError for misconfiguration and startup failures:
| Code | Condition |
|---|---|
| 400 | Invalid port, missing name, reserved name |
| 404 | Route file not found |
| 409 | Duplicate registration |
| 500 | Fastify/Socket.IO startup failure, instantiation error |