0
# Middleware System
1
2
Middleware integration system supporting both Express middleware and custom Inversify-based middleware with dependency injection support. The system allows for flexible middleware composition at both controller and method levels.
3
4
## Capabilities
5
6
### Base Middleware Class
7
8
Abstract base class for creating custom middleware that integrates with Inversify dependency injection.
9
10
```typescript { .api }
11
/**
12
* Abstract base class for middleware with dependency injection support
13
*/
14
abstract class BaseMiddleware {
15
/** HTTP context initialized when middleware is invoked */
16
httpContext: HttpContext;
17
18
/**
19
* Binds a service to the request-scoped container
20
* @param serviceIdentifier - Service identifier to bind
21
* @returns Binding syntax for fluent configuration
22
*/
23
protected bind<T>(
24
serviceIdentifier: interfaces.ServiceIdentifier<T>
25
): interfaces.BindingToSyntax<T>;
26
27
/**
28
* Abstract handler method that must be implemented by concrete middleware
29
* @param req - Express request object
30
* @param res - Express response object
31
* @param next - Express next function
32
*/
33
abstract handler(
34
req: Request,
35
res: Response,
36
next: NextFunction
37
): void | Promise<void>;
38
}
39
```
40
41
### Middleware Decorator
42
43
Decorator for applying middleware to controllers or individual methods.
44
45
```typescript { .api }
46
/**
47
* Applies middleware to a controller class or method
48
* @param middleware - Middleware functions or service identifiers to apply
49
* @returns Decorator function for classes or methods
50
*/
51
function withMiddleware(...middleware: Middleware[]): ClassDecorator | MethodDecorator;
52
```
53
54
### Middleware Types
55
56
Type definitions for middleware integration.
57
58
```typescript { .api }
59
/**
60
* Middleware type supporting both Express handlers and Inversify service identifiers
61
*/
62
type Middleware = interfaces.ServiceIdentifier | RequestHandler;
63
64
/**
65
* Middleware metadata interface for storing middleware configuration
66
*/
67
interface MiddlewareMetaData {
68
[identifier: string]: Middleware[];
69
}
70
```
71
72
**Usage Examples:**
73
74
```typescript
75
import { injectable } from "inversify";
76
import {
77
BaseMiddleware,
78
withMiddleware,
79
controller,
80
httpGet,
81
BaseHttpController
82
} from "inversify-express-utils";
83
84
// Custom Middleware with Dependency Injection
85
@injectable()
86
class LoggingMiddleware extends BaseMiddleware {
87
async handler(req: Request, res: Response, next: NextFunction): Promise<void> {
88
const startTime = Date.now();
89
90
// Access injected services through HTTP context
91
const logger = this.httpContext.container.get<Logger>("Logger");
92
93
logger.info(`${req.method} ${req.path} - Request started`);
94
95
// Continue to next middleware/handler
96
next();
97
98
// Log completion (this runs after the response)
99
res.on("finish", () => {
100
const duration = Date.now() - startTime;
101
logger.info(`${req.method} ${req.path} - Completed in ${duration}ms`);
102
});
103
}
104
}
105
106
@injectable()
107
class AuthenticationMiddleware extends BaseMiddleware {
108
async handler(req: Request, res: Response, next: NextFunction): Promise<void> {
109
const token = req.headers.authorization;
110
111
if (!token) {
112
res.status(401).json({ error: "Authentication required" });
113
return;
114
}
115
116
try {
117
// Use services from container
118
const authService = this.httpContext.container.get<AuthService>("AuthService");
119
const user = await authService.validateToken(token);
120
121
// Store user in request for later use
122
(req as any).user = user;
123
next();
124
} catch (error) {
125
res.status(401).json({ error: "Invalid token" });
126
}
127
}
128
}
129
130
@injectable()
131
class RateLimitMiddleware extends BaseMiddleware {
132
async handler(req: Request, res: Response, next: NextFunction): Promise<void> {
133
const cacheService = this.httpContext.container.get<CacheService>("CacheService");
134
const clientIp = req.ip;
135
const key = `rate_limit_${clientIp}`;
136
137
const requestCount = await cacheService.increment(key, 60); // 60 second window
138
139
if (requestCount > 100) { // 100 requests per minute
140
res.status(429).json({ error: "Rate limit exceeded" });
141
return;
142
}
143
144
next();
145
}
146
}
147
148
// Express Middleware Functions
149
function corsMiddleware(req: Request, res: Response, next: NextFunction) {
150
res.header("Access-Control-Allow-Origin", "*");
151
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
152
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
153
154
if (req.method === "OPTIONS") {
155
res.sendStatus(204);
156
return;
157
}
158
159
next();
160
}
161
162
function compressionMiddleware(req: Request, res: Response, next: NextFunction) {
163
// Simple compression logic
164
const originalSend = res.send;
165
res.send = function(body) {
166
if (typeof body === "string" && body.length > 1000) {
167
res.header("Content-Encoding", "gzip");
168
// Compress body (pseudo-code)
169
body = compress(body);
170
}
171
return originalSend.call(this, body);
172
};
173
next();
174
}
175
```
176
177
### Controller-Level Middleware
178
179
Apply middleware to all methods in a controller.
180
181
```typescript { .api }
182
// Using decorator syntax
183
@controller("/api/users")
184
@withMiddleware(LoggingMiddleware, AuthenticationMiddleware)
185
class UserController extends BaseHttpController {
186
// All methods inherit the middleware
187
188
@httpGet("/")
189
getUsers() {
190
return this.ok(["user1", "user2"]);
191
}
192
193
@httpPost("/")
194
createUser(@requestBody() userData: any) {
195
return this.created("/users/123", userData);
196
}
197
}
198
199
// Using constructor parameter
200
@controller("/api/products", LoggingMiddleware, corsMiddleware)
201
class ProductController extends BaseHttpController {
202
@httpGet("/")
203
getProducts() {
204
return this.ok(["product1", "product2"]);
205
}
206
}
207
```
208
209
### Method-Level Middleware
210
211
Apply middleware to specific methods within a controller.
212
213
```typescript { .api }
214
@controller("/api/admin")
215
class AdminController extends BaseHttpController {
216
@httpGet("/users")
217
@withMiddleware(AuthenticationMiddleware, RateLimitMiddleware)
218
getUsers() {
219
return this.ok(["user1", "user2"]);
220
}
221
222
@httpPost("/users")
223
@withMiddleware(AuthenticationMiddleware, compressionMiddleware)
224
createUser(@requestBody() userData: any) {
225
return this.created("/users/123", userData);
226
}
227
228
// No additional middleware on this method
229
@httpGet("/health")
230
getHealth() {
231
return this.ok({ status: "healthy" });
232
}
233
}
234
```
235
236
### Mixed Middleware Types
237
238
Combine Inversify middleware and Express middleware in the same application.
239
240
```typescript { .api }
241
@controller("/api/files")
242
@withMiddleware(corsMiddleware, LoggingMiddleware) // Express + Inversify middleware
243
class FileController extends BaseHttpController {
244
@httpPost("/upload")
245
@withMiddleware(AuthenticationMiddleware, compressionMiddleware)
246
uploadFile(@requestBody() fileData: any) {
247
return this.created("/files/123", fileData);
248
}
249
250
@httpGet("/:id")
251
@withMiddleware(RateLimitMiddleware)
252
getFile(@requestParam("id") id: string) {
253
return this.ok({ id, name: "file.txt" });
254
}
255
}
256
```
257
258
### Middleware Registration
259
260
Register custom middleware with the Inversify container.
261
262
**Usage Examples:**
263
264
```typescript
265
import { Container } from "inversify";
266
267
const container = new Container();
268
269
// Register middleware classes
270
container.bind<LoggingMiddleware>("LoggingMiddleware").to(LoggingMiddleware);
271
container.bind<AuthenticationMiddleware>("AuthenticationMiddleware").to(AuthenticationMiddleware);
272
container.bind<RateLimitMiddleware>("RateLimitMiddleware").to(RateLimitMiddleware);
273
274
// Register services that middleware depends on
275
container.bind<Logger>("Logger").to(ConsoleLogger);
276
container.bind<AuthService>("AuthService").to(JwtAuthService);
277
container.bind<CacheService>("CacheService").to(RedisCache);
278
279
// Create server with container
280
const server = new InversifyExpressServer(container);
281
282
// Configure global Express middleware
283
server.setConfig((app) => {
284
app.use(express.json());
285
app.use(express.urlencoded({ extended: true }));
286
// Note: Controller/method-specific middleware is handled automatically
287
});
288
```
289
290
### Middleware Execution Order
291
292
Middleware executes in the following order:
293
294
1. **Global app-level middleware** (configured via `setConfig()`)
295
2. **Controller-level middleware** (applied via `@controller()` or `@withMiddleware()`)
296
3. **Method-level middleware** (applied via method decorators)
297
4. **Route handler** (the actual controller method)
298
5. **Error middleware** (configured via `setErrorConfig()`)
299
300
**Usage Examples:**
301
302
```typescript
303
@controller("/api/data", corsMiddleware, LoggingMiddleware) // Middleware 2 & 3
304
class DataController extends BaseHttpController {
305
@httpGet("/")
306
@withMiddleware(AuthenticationMiddleware, RateLimitMiddleware) // Middleware 4 & 5
307
getData() { // Handler executes 6th
308
return this.ok({ data: "sample" });
309
}
310
}
311
312
// Execution order for GET /api/data:
313
// 1. Global middleware (express.json, etc.)
314
// 2. corsMiddleware
315
// 3. LoggingMiddleware
316
// 4. AuthenticationMiddleware
317
// 5. RateLimitMiddleware
318
// 6. getData() method
319
// 7. Error middleware (if any errors occur)
320
```
321
322
### Utility Functions
323
324
Utility functions for working with middleware metadata.
325
326
```typescript { .api }
327
/**
328
* Gets middleware metadata for a specific constructor and key
329
* @param constructor - Target constructor
330
* @param key - Metadata key
331
* @returns Array of middleware for the specified key
332
*/
333
function getMiddlewareMetadata(
334
constructor: DecoratorTarget,
335
key: string
336
): Middleware[];
337
```