@reldens/server-utils Architecture

Design philosophy and extension model for the @reldens/server-utils package.

Design Philosophy

@reldens/server-utils is an independent utility package that provides server infrastructure without coupling to other Reldens packages (like @reldens/utils).

Core Principles

1. Independence

  • No dependencies on @reldens/utils or other Reldens packages.
  • Self-contained utility classes.
  • Can be used standalone or integrated into Reldens.

2. Callback-Based Extensibility

The package provides hooks via callbacks instead of direct implementations.

Pattern:

  • Package provides callback properties.
  • Package calls these callbacks at specific points.
  • Application provides callback implementations.
  • Application wires callbacks to its own logging / monitoring systems.

3. Separation of Concerns

  • @reldens/server-utils: provides server infrastructure and callback hooks.
  • Application: provides implementations (logging, monitoring, error handling).
  • @reldens/utils: provides shared utilities (Logger, Shortcuts, etc.).

The package does NOT import Logger. Applications import Logger and wire it to package callbacks.

Available Callbacks

Four callbacks are available, set as configuration properties on AppServerFactory (which passes them to its configurers and to the HTTP/2 CDN server) or directly on Http2CdnServer: onError, onRequestSuccess, onRequestError and onEvent.

onError

Custom error handler for server errors. Called by the ServerErrorHandler static class, only when the callback is a function.

Event object:

  • key - error type identifier.
  • error - the error object.
  • The component instance, stored under the component name: appServerFactory, http2CdnServer or reverseProxyConfigurer.
  • The context fields, spread at the top level of the object (for example hostname, path, port, request, response).

Error points and their keys:

  • Virtual host resolution errors: virtual-host-no-hostname, virtual-host-unknown-domain.
  • SNI certificate loading errors: sni-key-read-failure, sni-cert-read-failure, and on the CDN certificate-key-not-found, certificate-not-found.
  • CDN server errors: server-error.
  • TLS client errors: tls-client-error.
  • Session errors: session-error.
  • Stream errors: stream-error, handle-stream-error.
  • HTTP/1 fallback errors: http1-stream-error, handle-http1-request-error.
  • Reverse proxy errors: proxy-error.

Example:

let config = {
    onError: (errorData) => {
        Logger.error('Server error:', errorData.key, errorData.error.message);
    }
};

onRequestSuccess

Called for successful HTTP requests (status code below 400). Called by the RequestLogger middleware, which is only installed when onRequestSuccess or onRequestError is set.

Data structure:

  • method - HTTP method (GET, POST, etc.).
  • path - request path.
  • statusCode - HTTP status code.
  • responseTime - response time in milliseconds.
  • ip - client IP address.
  • userAgent - client user agent.
  • hostname - requested host.
  • timestamp - ISO timestamp.

Example:

let config = {
    onRequestSuccess: (requestData) => {
        Logger.info('Request:', requestData.method, requestData.path, requestData.statusCode, requestData.responseTime+'ms');
    }
};

onRequestError

Called for failed HTTP requests (status code 400 or above), by the RequestLogger middleware, with the same data structure as onRequestSuccess.

Example:

let config = {
    onRequestError: (errorData) => {
        Logger.error('Request error:', errorData.method, errorData.path, errorData.statusCode);
    }
};

onEvent

Generic lifecycle event callback for server initialization and configuration events. Called by the EventDispatcher static class.

Data structure:

  • eventType - event type identifier.
  • instanceName - which component dispatched the event.
  • instance - the component instance.
  • data - event-specific data.
  • timestamp - ISO timestamp.

Event types:

  • App server:
    • app-server-created - Express app server created.
    • http-server-created - HTTP server created.
    • https-server-created - HTTPS server created.
    • sni-server-created - SNI server created.
    • app-server-listening - server started listening.
    • domain-added - virtual host domain added.
    • http2-cdn-created - HTTP/2 CDN server created.
  • Configurers:
    • development-mode-detected - development mode detected.
    • protocol-enforcement-enabled - protocol enforcement configured.
    • helmet-configured - Helmet security configured.
    • xss-protection-enabled - XSS protection configured.
    • cors-configured - CORS configured.
    • rate-limiting-configured - rate limiting configured.
    • reverse-proxy-configured - reverse proxy configured.
  • HTTP/2 CDN server:
    • cdn-server-created - CDN server instance created.
    • cdn-multi-cert-enabled / cdn-single-cert-mode - certificate mode selected.
    • cdn-sni-request / cdn-sni-fallback - SNI certificate selection, and fallback to the first domain.
    • cdn-handlers-setup - CDN event handlers configured.
    • cdn-server-listening - CDN server started listening.

Example:

let config = {
    onEvent: (eventData) => {
        Logger.debug('Event:', eventData.eventType, eventData.instanceName);
    }
};

Integration Pattern

const { AppServerFactory } = require('@reldens/server-utils');
const { Logger } = require('@reldens/utils');

let factory = new AppServerFactory();

let config = {
    port: 8080,
    onError: (errorData) => {
        Logger.error('Server error:', errorData.key, errorData.error.message);
    },
    onRequestSuccess: (requestData) => {
        Logger.info('Request:', requestData.method, requestData.path, requestData.statusCode);
    },
    onRequestError: (errorData) => {
        Logger.error('Request error:', errorData.method, errorData.path, errorData.statusCode);
    },
    onEvent: (eventData) => {
        Logger.debug('Event:', eventData.eventType);
    }
};

let serverResult = factory.createAppServer(config);

This pattern keeps the utility package independent while allowing full integration with application-specific logging and monitoring systems.

Integration in Reldens and the CMS

  • @reldens/cms: the CMS Manager adds a default onError callback that logs the error key, hostname, path and code through Logger, when useDefaultErrorCallback is enabled (the default) and the app server configuration has no onError of its own.
  • Reldens: ServerManager creates the app server from the server/appServerConfig configuration, built from the environment variables, without setting any callback. A project can add them from a reldens.createAppServer listener, which runs right before that configuration is passed to AppServerFactory.createAppServer().

This decoupling means @reldens/server-utils can be used in projects that have their own logger or no logger at all.

Related Documentation

Go Up