Middleware
Empack middleware works similarly to Express middleware but with support for:
- Class-based middleware with full Dependency Injection (DI)
- Route-level、controller-level and app-level middleware
- request-scoped dependencies
Middleware Types
There are two main ways to define middleware in Empack:
1. Function-style middleware (Express-compatible)
const loggerMiddleware: EmpackMiddlewareFunction = (req, res, next) => {
console.log(`[${req.method}] ${req.url}`);
next();
};
app.useMiddleware(loggerMiddleware);
This style is mostly used for global middleware, such as CORS, parsers, static serving, etc.
2. Class-style middleware (with DI)
export class LoggerMiddleware implements IEmpackMiddleware {
constructor(private logger: Logger) {}
async use(req: Request, res: Response, next: NextFunction) {
this.logger.log(`[${req.method}] ${req.url}`);
next();
}
}
To apply this middleware to a controller:
@Controller("/user", LoggerMiddleware)
export class UserController {
@Get("/")
getUser() {
return Responses.OK("User route")
}
}
NOTE
Class-based middleware is only available at controller/route level, and supports full DI — including request-scoped classes.
Global Middleware Cannot Use DI
Global middleware registered via app.useMiddleware(...) must be function-style. They do not go through Empack's container system and cannot use dependency injection.
// ✅ Allowed
app.useMiddleware((req, res, next) => {
console.log("Global middleware");
next();
});
// ❌ Not supported
app.useMiddleware(SomeClassBasedMiddleware); // will not be injected
Middleware Execution Order
Empack runs middleware in a well-defined order:
- App-level middleware (
app.useMiddleware(...)
) - Controller-level middleware (
@Controller("/", ...middleware)
) - Route-level middleware (
@Get("/", ...middleware)
)
This means:
- App-level middleware runs for all routes
- Controller-level middleware runs only for that controller’s routes
- Route-level middleware runs only for that specific route
Here’s a typical middleware execution flow:
[Global App Middleware]
↓
[Guard Middleware(if any)]
↓
[Controller Middleware]
↓
[Route Middleware]
This ensures shared logic (like logging or headers) can be applied globally, while per-controller or per-route logic stays modular and focused.