npm-express

Description
Fast, unopinionated, minimalist web framework for Node.js
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/npm-express@4.21.0

middleware.md docs/

1
# Middleware System
2
3
Middleware functions for request processing, authentication, logging, body parsing, and request/response modification in the Express request-response cycle.
4
5
## Capabilities
6
7
### Middleware Mounting
8
9
Mount middleware functions to execute during request processing with optional path filtering.
10
11
```javascript { .api }
12
/**
13
* Mount middleware function(s) at optional path
14
* @param {string} [path] - Optional path to mount middleware (default: '/')
15
* @param {...RequestHandler} middleware - Middleware function(s) to mount
16
* @returns {Application} Application instance for chaining
17
*/
18
app.use(path?: string, ...middleware: RequestHandler[]): Application;
19
20
/**
21
* Middleware function signature
22
* @param {Request} req - Express request object
23
* @param {Response} res - Express response object
24
* @param {NextFunction} next - Function to pass control to next middleware
25
*/
26
type RequestHandler = (req: Request, res: Response, next: NextFunction) => void | Promise<void>;
27
28
/**
29
* Error handling middleware signature
30
* @param {Error} err - Error object
31
* @param {Request} req - Express request object
32
* @param {Response} res - Express response object
33
* @param {NextFunction} next - Function to pass control to next middleware
34
*/
35
type ErrorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => void;
36
```
37
38
**Usage Examples:**
39
40
```javascript
41
const app = express();
42
43
// Global middleware (runs for all routes)
44
app.use((req, res, next) => {
45
console.log(`${req.method} ${req.url} - ${new Date()}`);
46
next();
47
});
48
49
// Path-specific middleware
50
app.use('/api', (req, res, next) => {
51
req.apiVersion = 'v1';
52
next();
53
});
54
55
// Multiple middleware functions
56
app.use('/admin', authenticate, authorize, (req, res, next) => {
57
res.locals.isAdmin = true;
58
next();
59
});
60
61
// Error handling middleware (must be last)
62
app.use((err, req, res, next) => {
63
console.error(err.stack);
64
res.status(500).send('Something broke!');
65
});
66
```
67
68
### Built-in Body Parsers
69
70
Parse incoming request bodies into JavaScript objects accessible via `req.body`.
71
72
```javascript { .api }
73
/**
74
* Parse JSON request bodies
75
* @param {JsonOptions} [options] - JSON parser options
76
* @returns {RequestHandler} Middleware function
77
*/
78
express.json(options?: JsonOptions): RequestHandler;
79
80
/**
81
* Parse URL-encoded request bodies (form data)
82
* @param {UrlencodedOptions} [options] - URL-encoded parser options
83
* @returns {RequestHandler} Middleware function
84
*/
85
express.urlencoded(options?: UrlencodedOptions): RequestHandler;
86
87
/**
88
* Parse raw request bodies into Buffer
89
* @param {RawOptions} [options] - Raw parser options
90
* @returns {RequestHandler} Middleware function
91
*/
92
express.raw(options?: RawOptions): RequestHandler;
93
94
/**
95
* Parse text request bodies into string
96
* @param {TextOptions} [options] - Text parser options
97
* @returns {RequestHandler} Middleware function
98
*/
99
express.text(options?: TextOptions): RequestHandler;
100
101
/**
102
* JSON parser options
103
*/
104
interface JsonOptions {
105
/** Request size limit (default: '100kb') */
106
limit?: string | number;
107
/** Inflate compressed bodies (default: true) */
108
inflate?: boolean;
109
/** Only parse objects and arrays (default: true) */
110
strict?: boolean;
111
/** Custom reviver function */
112
reviver?: (key: string, value: any) => any;
113
/** Verify function for raw body */
114
verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;
115
}
116
117
/**
118
* URL-encoded parser options
119
*/
120
interface UrlencodedOptions {
121
/** Request size limit (default: '100kb') */
122
limit?: string | number;
123
/** Use extended syntax with qs library (default: true) */
124
extended?: boolean;
125
/** Maximum parameter count (default: 1000) */
126
parameterLimit?: number;
127
/** Verify function for raw body */
128
verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;
129
}
130
131
/**
132
* Raw parser options
133
*/
134
interface RawOptions {
135
/** Request size limit (default: '100kb') */
136
limit?: string | number;
137
/** Expected content type (default: 'application/octet-stream') */
138
type?: string | string[] | ((req: Request) => boolean);
139
/** Verify function for raw body */
140
verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;
141
}
142
143
/**
144
* Text parser options
145
*/
146
interface TextOptions {
147
/** Request size limit (default: '100kb') */
148
limit?: string | number;
149
/** Text encoding (default: 'utf8') */
150
defaultCharset?: string;
151
/** Expected content type (default: 'text/plain') */
152
type?: string | string[] | ((req: Request) => boolean);
153
/** Verify function for raw body */
154
verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;
155
}
156
```
157
158
**Usage Examples:**
159
160
```javascript
161
const app = express();
162
163
// Parse JSON bodies
164
app.use(express.json());
165
166
// Parse URL-encoded bodies (form data)
167
app.use(express.urlencoded({ extended: true }));
168
169
// Parse raw bodies for specific routes
170
app.use('/upload', express.raw({ type: 'application/octet-stream', limit: '10mb' }));
171
172
// Parse text bodies
173
app.use('/webhook', express.text({ type: 'text/plain' }));
174
175
// Custom JSON parsing with options
176
app.use(express.json({
177
limit: '50mb',
178
verify: (req, res, buf, encoding) => {
179
// Verify request body before parsing
180
req.rawBody = buf;
181
}
182
}));
183
184
// Routes can now access req.body
185
app.post('/api/users', (req, res) => {
186
console.log(req.body); // Parsed JSON object
187
res.json({ received: req.body });
188
});
189
```
190
191
### Static File Serving
192
193
Serve static files from a directory with caching and security options.
194
195
```javascript { .api }
196
/**
197
* Serve static files from root directory
198
* @param {string} root - Root directory to serve files from
199
* @param {StaticOptions} [options] - Static serving options
200
* @returns {RequestHandler} Middleware function
201
*/
202
express.static(root: string, options?: StaticOptions): RequestHandler;
203
204
/**
205
* Static file serving options
206
*/
207
interface StaticOptions {
208
/** Enable dot files (default: false) */
209
dotfiles?: 'allow' | 'deny' | 'ignore';
210
/** Set ETag header (default: true) */
211
etag?: boolean;
212
/** File extensions to try (default: false) */
213
extensions?: string[] | false;
214
/** Fallthrough to next handler on 404 (default: true) */
215
fallthrough?: boolean;
216
/** Immutable cache control (default: false) */
217
immutable?: boolean;
218
/** Default file to serve for directories (default: 'index.html') */
219
index?: string | string[] | false;
220
/** Last-Modified header (default: true) */
221
lastModified?: boolean;
222
/** Cache-Control max-age in milliseconds (default: 0) */
223
maxAge?: number | string;
224
/** Redirect to trailing slash (default: true) */
225
redirect?: boolean;
226
/** Function to set custom headers */
227
setHeaders?: (res: Response, path: string, stat: any) => void;
228
}
229
```
230
231
**Usage Examples:**
232
233
```javascript
234
const app = express();
235
const path = require('path');
236
237
// Serve static files from 'public' directory
238
app.use(express.static('public'));
239
240
// Serve from specific path
241
app.use('/static', express.static(path.join(__dirname, 'assets')));
242
243
// Multiple static directories
244
app.use(express.static('public'));
245
app.use(express.static('uploads'));
246
247
// Static files with options
248
app.use('/files', express.static('documents', {
249
dotfiles: 'deny',
250
index: false,
251
maxAge: '1d',
252
setHeaders: (res, path) => {
253
if (path.endsWith('.pdf')) {
254
res.set('Content-Type', 'application/pdf');
255
}
256
}
257
}));
258
```
259
260
### Query String Parsing
261
262
Create custom query string parsing middleware. Note: Express has built-in query parsing, so this is only needed for custom parsing logic.
263
264
```javascript { .api }
265
/**
266
* Create query string parsing middleware
267
* @param {QueryOptions | Function} [options] - Query parser options or custom parser function
268
* @returns {RequestHandler} Middleware function
269
*/
270
express.query(options?: QueryOptions | Function): RequestHandler;
271
272
/**
273
* Query parser options
274
*/
275
interface QueryOptions {
276
/** Maximum number of parameters (default: 1000) */
277
parameterLimit?: number;
278
/** Array format parsing */
279
arrayFormat?: 'brackets' | 'indices' | 'comma' | 'separator';
280
/** Delimiter for arrays */
281
delimiter?: string;
282
/** Parse numbers (default: false) */
283
parseNumbers?: boolean;
284
/** Parse booleans (default: false) */
285
parseBooleans?: boolean;
286
}
287
```
288
289
**Usage Examples:**
290
291
```javascript
292
const app = express();
293
294
// Express automatically parses query strings (no middleware needed)
295
// GET /search?q=express&category=web&limit=10
296
app.get('/search', (req, res) => {
297
console.log(req.query);
298
// { q: 'express', category: 'web', limit: '10' }
299
res.json(req.query);
300
});
301
302
// Only use express.query for custom parsing logic
303
app.use('/custom', express.query((str, options) => {
304
// Custom query string parsing logic
305
return customQueryParser(str);
306
}));
307
308
// GET /products?active=true&price=99&tags=web,framework
309
app.get('/products', (req, res) => {
310
console.log(req.query);
311
// { active: true, price: 99, tags: 'web,framework' }
312
res.json(req.query);
313
});
314
```
315
316
### Parameter Middleware
317
318
Middleware that executes when specific route parameters are encountered.
319
320
```javascript { .api }
321
/**
322
* Add callback triggers for route parameters
323
* @param {string} name - Parameter name to trigger on
324
* @param {ParamHandler} handler - Parameter handler function
325
* @returns {Application} Application instance for chaining
326
*/
327
app.param(name: string, handler: ParamHandler): Application;
328
329
/**
330
* Parameter handler function signature
331
* @param {Request} req - Express request object
332
* @param {Response} res - Express response object
333
* @param {NextFunction} next - Next middleware function
334
* @param {any} value - Parameter value from URL
335
* @param {string} name - Parameter name
336
*/
337
type ParamHandler = (req: Request, res: Response, next: NextFunction, value: any, name: string) => void;
338
```
339
340
**Usage Examples:**
341
342
```javascript
343
// Parameter middleware for user loading
344
app.param('userId', async (req, res, next, id) => {
345
try {
346
const user = await User.findById(id);
347
if (!user) {
348
return res.status(404).json({ error: 'User not found' });
349
}
350
req.user = user;
351
next();
352
} catch (error) {
353
next(error);
354
}
355
});
356
357
// Parameter validation middleware
358
app.param('id', (req, res, next, id) => {
359
if (!/^\d+$/.test(id)) {
360
return res.status(400).json({ error: 'ID must be numeric' });
361
}
362
next();
363
});
364
365
// Routes using the parameters
366
app.get('/users/:userId', (req, res) => {
367
// req.user is already loaded
368
res.json(req.user);
369
});
370
371
app.get('/posts/:id', (req, res) => {
372
// ID is already validated
373
res.json({ id: req.params.id });
374
});
375
```
376
377
### Custom Middleware Patterns
378
379
Common middleware patterns for cross-cutting concerns.
380
381
**Usage Examples:**
382
383
```javascript
384
// Request logging middleware
385
const logger = (req, res, next) => {
386
const start = Date.now();
387
res.on('finish', () => {
388
const duration = Date.now() - start;
389
console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);
390
});
391
next();
392
};
393
394
// Authentication middleware
395
const authenticate = (req, res, next) => {
396
const token = req.headers.authorization?.split(' ')[1];
397
if (!token) {
398
return res.status(401).json({ error: 'No token provided' });
399
}
400
401
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
402
if (err) {
403
return res.status(401).json({ error: 'Invalid token' });
404
}
405
req.user = decoded;
406
next();
407
});
408
};
409
410
// CORS middleware
411
const cors = (req, res, next) => {
412
res.header('Access-Control-Allow-Origin', '*');
413
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
414
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
415
416
if (req.method === 'OPTIONS') {
417
return res.sendStatus(200);
418
}
419
next();
420
};
421
422
// Rate limiting middleware
423
const rateLimit = (windowMs, max) => {
424
const requests = new Map();
425
426
return (req, res, next) => {
427
const ip = req.ip;
428
const now = Date.now();
429
430
if (!requests.has(ip)) {
431
requests.set(ip, []);
432
}
433
434
const ipRequests = requests.get(ip);
435
const recentRequests = ipRequests.filter(time => now - time < windowMs);
436
437
if (recentRequests.length >= max) {
438
return res.status(429).json({ error: 'Too many requests' });
439
}
440
441
recentRequests.push(now);
442
requests.set(ip, recentRequests);
443
next();
444
};
445
};
446
447
// Use middleware
448
app.use(logger);
449
app.use(cors);
450
app.use('/api', rateLimit(15 * 60 * 1000, 100)); // 100 requests per 15 minutes
451
app.use('/protected', authenticate);
452
```
453
454
## Middleware Execution Order
455
456
Middleware executes in the order it's defined, making order important for proper functionality.
457
458
```javascript
459
const app = express();
460
461
// 1. First: CORS headers
462
app.use(cors);
463
464
// 2. Second: Request logging
465
app.use(logger);
466
467
// 3. Third: Body parsing
468
app.use(express.json());
469
470
// 4. Fourth: Authentication (for protected routes)
471
app.use('/api/protected', authenticate);
472
473
// 5. Fifth: Route handlers
474
app.get('/api/users', getUsers);
475
476
// 6. Last: Error handling
477
app.use(errorHandler);
478
```