ExpressKernelServer
Express runtime adapter using routing-controllers.
import { ExpressKernelServer } from '@haskou/ddd-kernel/adapters/ui/express';
const server = new ExpressKernelServer({ kernel, port: 3000 });
await server.run();Routes are registered with kernel.registerRoutes(RouteClass).
The adapter can be configured either through constructor options or by calling registration methods before run(). The methods return the server instance, so they can be chained.
ExpressKernelServer resolves express and routing-controllers from the consumer application's process.cwd(). This avoids duplicate runtime copies when the package is installed through a local file: dependency.
External Controllers
Applications can add controllers at the server boundary without registering them on the kernel:
const server = new ExpressKernelServer({
controllers: [HealthController],
kernel,
port: 3000,
});controllers are merged with kernel.getRoutes() before routing-controllers is configured.
The same can be done after construction:
server.registerControllers(HealthController, MetricsController);Routing Controllers Options
Use routingControllersOptions to pass options directly to useExpressServer. controllers and routePrefix are still owned by ExpressKernelServer:
const server = new ExpressKernelServer({
kernel,
routingControllersOptions: {
cors: true,
defaultErrorHandler: false,
middlewares: [HttpRequestContextMiddleware],
},
});This is the place for routing-controllers behavior such as CORS, disabling its default error handler, or registering routing-controllers middleware classes. When enabling cors, install cors in the application; it is an optional peer dependency because HTTP support is optional.
HTTP Middleware And Hooks
Use middleware arrays for normal Express middleware and hooks for integrations that need direct app access, such as Swagger or static assets:
const server = new ExpressKernelServer({
kernel,
hooks: [
{ phase: 'beforeControllers', handle: setupTracing },
{ phase: 'beforeErrors', handle: setupSwagger },
{ phase: 'beforeErrors', handle: setupStaticAssets },
],
middlewares: [requestIdMiddleware],
preControllerMiddlewares: [authenticationMiddleware],
postControllerMiddlewares: [notFoundMiddleware],
});Hook order is:
middlewarespreControllerMiddlewaresbeforeControllersHookshookswithphase: 'beforeControllers'routing-controllerspostControllerMiddlewaresafterControllersHookshookswithphase: 'afterControllers'swaggerHooksstaticHookshookswithphase: 'beforeErrors'errorHandlers
swaggerHooks and staticHooks remain available for compatibility. New integrations should use hooks with an explicit phase.
You can also register the pipeline imperatively before the server starts:
const server = new ExpressKernelServer({ kernel, port: 3000 });
server
.registerMiddlewares(requestIdMiddleware)
.registerPreControllerMiddlewares(authenticationMiddleware)
.registerHooks({
phase: 'beforeErrors',
handle: (app) => {
app.get('/health', (request, response) => {
void request;
response.status(200).json({ status: 'ok' });
});
},
})
.registerPostControllerMiddlewares(notFoundMiddleware);
await server.run();Registration methods must be called before run(). Once the server is running, the Express pipeline is fixed and further registration throws an error.
Error Handlers
Use errorHandlers or registerErrorHandlers() for normal Express error middleware:
import type { ErrorRequestHandler } from 'express';
const httpErrorHandler: ErrorRequestHandler = (
error,
request,
response,
next,
) => {
void request;
if (response.headersSent) {
next(error);
return;
}
response.status(error.statusCode ?? 500).json({
error: error.name ?? 'InternalServerError',
message: error.message ?? 'Unexpected error',
});
};
const server = new ExpressKernelServer({ kernel });
server.registerErrorHandlers(httpErrorHandler);Error handlers run after:
- global middleware
- pre-controller middleware
- controllers
- post-controller middleware
- hooks registered for
beforeErrors
If no error handler is registered, ExpressKernelServer uses a default handler that returns a 500 JSON response.
The Express adapter also exports HttpErrorHandler, a reusable handler for the common cases covered by routing-controllers and Express:
import {
ExpressKernelServer,
HttpErrorHandler,
} from '@haskou/ddd-kernel/adapters/ui/express';
const server = new ExpressKernelServer({ kernel });
server.registerErrorHandlers(
new HttpErrorHandler({ logger: kernel.logger }).handle,
);It maps malformed JSON to 400, payload-too-large errors to 413, HTTP errors with httpCode, statusCode or status to their status code, and unexpected errors to 500. Validation errors exposed through an errors array are flattened into the response body.
Applications can prepend domain-specific mappings with handlers:
server.registerErrorHandlers(
new HttpErrorHandler({
handlers: [
(error, response) => {
if (error.name !== 'DomainError') {
return false;
}
response.status(409).json({
code: error.name,
message: error.message,
});
return true;
},
],
logger: kernel.logger,
}).handle,
);Custom handlers run after malformed JSON and payload-too-large handling, and before the generic HTTP/unhandled fallbacks.
Registration API
ExpressKernelServer exposes these registration methods:
server.registerControllers(...controllers);
server.registerMiddlewares(...middlewares);
server.registerPreControllerMiddlewares(...middlewares);
server.registerPostControllerMiddlewares(...middlewares);
server.registerHooks(...hooks);
server.registerBeforeControllersHooks(...hooks);
server.registerAfterControllersHooks(...hooks);
server.registerErrorHandlers(...handlers);