DI Integration
The bindHttpFactory function turns a controller method into an HttpHandlerFn. This lets you keep HTTP handlers on injectable controller classes and resolve them from a DI container for every request, instead of wiring handler functions to service instances manually.
bindHttpFactory(container) returns a binder with the signature (token, method) => HttpHandlerFn:
token— the token of the controller to resolve.method— the name of the handler method to invoke.
For each request, the binder:
- Resolves the controller with
container.resolveOrFail(token), so the controller follows the lifetime of its registration. - Invokes the bound method with the handler arguments.
- Throws
UnexpectedErrorwhen the resolved member is not invocable.
Declare handler methods as arrow function properties so this stays bound to the controller instance when the binder invokes them.
Initial configuration
Set up the container, the router, and the binder created with bindHttpFactory:
import { Container } from "eridu-tech/di";
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";
import { HttpRouter, defaultHttpRouterAdapter } from "eridu-tech/http-router";
import {
bindHttpFactory,
bindHttpMiddlewareFactory,
} from "eridu-tech/http-router/di";
const executionContext = new ExecutionContext(new AlsExecutionContextAdapter());
export const container = new Container({ executionContext });
export const router = new HttpRouter({ router: defaultHttpRouterAdapter() });
export const bindHttp = bindHttpFactory(container);
export const bindHttpMiddleware = bindHttpMiddlewareFactory(container);
Then define the controller and register it in the container:
import type { HttpHandlerFn } from "eridu-tech/http-router/contracts";
import { container } from "./container.js";
export class UsersController {
getUser: HttpHandlerFn = ({ req, json }) => {
const { id } = req.params();
return json({ id });
};
}
container.registerValue({
token: UsersController,
value: new UsersController(),
});
await container.init();
Usage
Registering endpoints
Pass bindHttp(Controller, "methodName") wherever a handler is accepted:
import { bindHttp, router } from "./container.js";
import { UsersController } from "./users-controller.js";
router.endpoint({
url: "/users/:id",
method: ["GET"],
handler: bindHttp(UsersController, "getUser"),
});
Inside route groups
The bound handler is a regular handler, so it can be registered inside route groups:
import { bindHttp, router } from "./container.js";
import { UsersController } from "./users-controller.js";
router.group("/api", (api) => {
api.endpoint({
url: "/users/:id",
method: ["GET"],
handler: bindHttp(UsersController, "getUser"),
});
});
Binding middlewares
The bindHttpMiddlewareFactory function turns a middleware registered in the container into an HttpMiddlewareFn, so middlewares can be classes or values with their own dependencies instead of hand-wired instances.
bindHttpMiddlewareFactory(container) returns a binder with the signature (token) => HttpMiddlewareFn, where token is the token of the middleware to resolve. For each request, the binder:
- Resolves the middleware with
container.resolveOrFail(token), so the middleware follows the lifetime of its registration. - Invokes the middleware with the middleware arguments (
req,res,contextandnext). - Throws
UnexpectedErrorwhen the resolved middleware is not invocable.
Both middleware functions and invocable objects (values with an invoke method) are supported.
Register the middleware in the container:
import { genericToken } from "eridu-tech/di/contracts";
import { container } from "./container.js";
import type { HttpMiddleware } from "eridu-tech/http-router/contracts";
export const requestLoggerToken = genericToken<HttpMiddleware>("requestLogger");
container.registerValue({
token: requestLoggerToken,
value: {
async invoke({ next, req }) {
console.log(`${req.method} ${req.url}`);
return next();
},
},
});
Then pass bindHttpMiddleware(token) wherever a middleware is accepted — to router.use() for shared middleware:
import { bindHttpMiddleware, router } from "./container.js";
import { requestLoggerToken } from "./middleware.js";
router.use(bindHttpMiddleware(requestLoggerToken));
Or to the middlewares of a single endpoint:
import { bindHttp, bindHttpMiddleware, router } from "./container.js";
import { requestLoggerToken } from "./middleware.js";
import { UsersController } from "./users-controller.js";
router.endpoint({
url: "/users/:id",
method: ["GET"],
middlewares: (builder) =>
builder.use(bindHttpMiddleware(requestLoggerToken)),
handler: bindHttp(UsersController, "getUser"),
});
Further information
For further information refer to:
eridu-tech/http-routerAPI docs.- DI Container usage for the container lifecycle and registration methods.