@fishka/express 0.9.23 → 0.9.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +227 -106
- package/dist/cjs/auth/auth.utils.d.ts +20 -12
- package/dist/cjs/auth/auth.utils.js +35 -29
- package/dist/cjs/auth/auth.utils.js.map +1 -1
- package/dist/cjs/error-handling.d.ts +21 -0
- package/dist/cjs/error-handling.js +64 -0
- package/dist/cjs/error-handling.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -2
- package/dist/cjs/index.js +1 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/request-utils.d.ts +29 -0
- package/dist/cjs/request-utils.js +115 -0
- package/dist/cjs/request-utils.js.map +1 -0
- package/dist/cjs/thread-local/thread-local-storage-middleware.d.ts +1 -1
- package/dist/cjs/thread-local/thread-local-storage-middleware.js +14 -2
- package/dist/cjs/thread-local/thread-local-storage-middleware.js.map +1 -1
- package/dist/cjs/utils/type-validators.d.ts +1 -1
- package/dist/cjs/utils/type-validators.js +1 -1
- package/dist/cjs/utils/type-validators.js.map +1 -1
- package/dist/esm/auth/auth.utils.d.ts +20 -12
- package/dist/esm/auth/auth.utils.js +35 -29
- package/dist/esm/auth/auth.utils.js.map +1 -1
- package/dist/esm/error-handling.d.ts +21 -0
- package/dist/esm/error-handling.js +63 -0
- package/dist/esm/error-handling.js.map +1 -1
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +1 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/request-utils.d.ts +29 -0
- package/dist/esm/request-utils.js +77 -0
- package/dist/esm/request-utils.js.map +1 -0
- package/dist/esm/thread-local/thread-local-storage-middleware.d.ts +1 -1
- package/dist/esm/thread-local/thread-local-storage-middleware.js +15 -3
- package/dist/esm/thread-local/thread-local-storage-middleware.js.map +1 -1
- package/dist/esm/utils/type-validators.d.ts +1 -1
- package/dist/esm/utils/type-validators.js +1 -1
- package/dist/esm/utils/type-validators.js.map +1 -1
- package/package.json +1 -1
- package/dist/cjs/route-table.d.ts +0 -30
- package/dist/cjs/route-table.js +0 -40
- package/dist/cjs/route-table.js.map +0 -1
- package/dist/cjs/router.d.ts +0 -86
- package/dist/cjs/router.js +0 -220
- package/dist/cjs/router.js.map +0 -1
- package/dist/esm/route-table.d.ts +0 -30
- package/dist/esm/route-table.js +0 -36
- package/dist/esm/route-table.js.map +0 -1
- package/dist/esm/router.d.ts +0 -86
- package/dist/esm/router.js +0 -184
- package/dist/esm/router.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Express API
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Functional utilities for Express.js with type-safe validation and error handling.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,65 +8,165 @@ Type-safe Express.js routing with clean, minimal API.
|
|
|
8
8
|
npm install @fishka/express
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
This package provides standalone utility functions that work directly with Express. Each feature can be used independently - mix and match what you need.
|
|
14
|
+
|
|
11
15
|
## Quick Start
|
|
12
16
|
|
|
13
17
|
```typescript
|
|
14
18
|
import express from 'express';
|
|
15
|
-
import {
|
|
19
|
+
import { addErrorHandling, body, patchExpressAsyncErrors, pathParam, queryParam } from '@fishka/express';
|
|
16
20
|
import { assertString } from '@fishka/assertions';
|
|
17
21
|
|
|
18
22
|
const app = express();
|
|
19
23
|
app.use(express.json());
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
// Enable automatic async error catching (call once at startup)
|
|
26
|
+
patchExpressAsyncErrors();
|
|
22
27
|
|
|
23
28
|
// GET /users/:id - with typed path params
|
|
24
|
-
|
|
25
|
-
id
|
|
26
|
-
name: 'John'
|
|
27
|
-
})
|
|
29
|
+
app.get('/users/:id', async (req, res) => {
|
|
30
|
+
const id = pathParam(req, 'id'); // string - validated inline
|
|
31
|
+
res.json({ id, name: 'John' });
|
|
32
|
+
});
|
|
28
33
|
|
|
29
|
-
// GET /users -
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
+
// GET /users - with query params
|
|
35
|
+
app.get('/users', async (req, res) => {
|
|
36
|
+
const page = queryParam(req, 'page'); // string - throws 400 if missing
|
|
37
|
+
res.json({ page, users: [] });
|
|
38
|
+
});
|
|
34
39
|
|
|
35
40
|
// POST /users - with body validation
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
})
|
|
41
|
+
app.post('/users', async (req, res) => {
|
|
42
|
+
const data = body(req, { name: assertString });
|
|
43
|
+
res.json({ id: 1, name: data.name }); // validators infer a type for the data!
|
|
44
|
+
});
|
|
40
45
|
|
|
41
|
-
//
|
|
42
|
-
|
|
46
|
+
// Add error handling middleware (must be last)
|
|
47
|
+
addErrorHandling(app);
|
|
43
48
|
|
|
44
49
|
app.listen(3000);
|
|
45
50
|
```
|
|
46
51
|
|
|
47
|
-
##
|
|
52
|
+
## Utilities
|
|
53
|
+
|
|
54
|
+
### `pathParam(req, name, validator?)`
|
|
48
55
|
|
|
49
|
-
|
|
56
|
+
Get and validate a path parameter from Express request.
|
|
50
57
|
|
|
51
58
|
```typescript
|
|
52
|
-
import { transform, toInt, minLength
|
|
59
|
+
import { pathParam, transform, toInt, minLength } from '@fishka/express';
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
app.get('/users/:id', async (req, res) => {
|
|
62
|
+
// Simple - returns string, throws 400 if missing
|
|
63
|
+
const id1 = pathParam(req, 'id');
|
|
64
|
+
|
|
65
|
+
// With validation - transform to number
|
|
66
|
+
const id2 = pathParam(req, 'id', transform(toInt()));
|
|
67
|
+
|
|
68
|
+
// Transform with validation - string min length
|
|
69
|
+
const slug = pathParam(req, 'slug', transform(minLength(3)));
|
|
70
|
+
|
|
71
|
+
res.json({ id: '...' });
|
|
72
|
+
});
|
|
61
73
|
```
|
|
62
74
|
|
|
63
|
-
###
|
|
75
|
+
### `queryParam(req, name, validator?)`
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
Get and validate a query parameter from Express request.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { queryParam, transform, toInt, min, range, oneOf } from '@fishka/express';
|
|
81
|
+
|
|
82
|
+
app.get('/users', async (req, res) => {
|
|
83
|
+
// Simple - returns string, throws 400 if missing
|
|
84
|
+
const search = queryParam(req, 'search');
|
|
85
|
+
|
|
86
|
+
// With validation - transform to number with min value
|
|
87
|
+
const page = queryParam(req, 'page', transform(toInt(), min(1)));
|
|
88
|
+
|
|
89
|
+
// With validation - number range
|
|
90
|
+
const limit = queryParam(req, 'limit', transform(toInt(), range(1, 100)));
|
|
91
|
+
|
|
92
|
+
// With validation - enum
|
|
93
|
+
const sort = queryParam(req, 'sort', transform(oneOf('asc', 'desc')));
|
|
94
|
+
|
|
95
|
+
res.json({ page, limit, users: [] });
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `body(req, validator)`
|
|
100
|
+
|
|
101
|
+
Get and validate the request body.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { body } from '@fishka/express';
|
|
105
|
+
import { assertString, assertNumber } from '@fishka/assertions';
|
|
106
|
+
|
|
107
|
+
app.post('/users', async (req, res) => {
|
|
108
|
+
// Object assertion - validates and infers a type.
|
|
109
|
+
const data = body(req, {
|
|
110
|
+
name: assertString,
|
|
111
|
+
age: assertNumber,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
res.json({ id: 1, name: data.name });
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `patchExpressAsyncErrors()`
|
|
119
|
+
|
|
120
|
+
Patches Express to automatically catch async errors. Call once at application startup.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { patchExpressAsyncErrors } from '@fishka/express';
|
|
124
|
+
|
|
125
|
+
// Call before defining routes
|
|
126
|
+
patchExpressAsyncErrors();
|
|
127
|
+
|
|
128
|
+
// Now async route handlers don't need try/catch
|
|
129
|
+
app.get('/users', async (req, res) => {
|
|
130
|
+
const users = await db.users.findAll(); // Errors are caught automatically
|
|
131
|
+
res.json(users);
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `addErrorHandling(app)`
|
|
136
|
+
|
|
137
|
+
Adds centralized error handling middleware. Must be called after all routes.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { addErrorHandling, HttpError } from '@fishka/express';
|
|
141
|
+
|
|
142
|
+
// Define routes...
|
|
143
|
+
app.get('/users/:id', async (req, res) => {
|
|
144
|
+
const user = await db.users.findById(req.params.id);
|
|
145
|
+
assertHttp(user, 404, 'User not found');
|
|
146
|
+
res.json(user);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Add error handling last
|
|
150
|
+
addErrorHandling(app);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Parameter Validation
|
|
154
|
+
|
|
155
|
+
Use `transform()` to validate and transform parameters. All operators are composable:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { transform, toInt, minLength, matches, min, range, oneOf } from '@fishka/express';
|
|
159
|
+
|
|
160
|
+
app.get('/users/:id', async (req, res) => {
|
|
161
|
+
const id = pathParam(req, 'id', transform(toInt())); // string → number
|
|
162
|
+
const page = queryParam(req, 'page', transform(toInt(), min(1))); // number >= 1
|
|
163
|
+
const limit = queryParam(req, 'limit', transform(toInt(), range(1, 100))); // number 1-100
|
|
164
|
+
const sort = queryParam(req, 'sort', transform(oneOf('asc', 'desc'))); // enum
|
|
165
|
+
const search = queryParam(req, 'search', transform(minLength(3))); // string min 3 chars
|
|
166
|
+
|
|
167
|
+
res.json({ id, page, limit });
|
|
168
|
+
});
|
|
169
|
+
```
|
|
70
170
|
|
|
71
171
|
### Available Operators
|
|
72
172
|
|
|
@@ -98,26 +198,12 @@ routes.get('users/:id', async ctx => ({
|
|
|
98
198
|
- `validator(fn)` - custom validator returning string|undefined
|
|
99
199
|
- `map(fn)` - transform value
|
|
100
200
|
|
|
101
|
-
###
|
|
102
|
-
|
|
103
|
-
- **Path parameters** are always required - `ctx.path()` throws 400 if missing
|
|
104
|
-
- **Query parameters** are always required - `ctx.query()` throws 400 if missing/empty
|
|
105
|
-
- **Validators** transform and validate parameter values
|
|
106
|
-
|
|
107
|
-
For parameters that should have default values when missing, handle them at the application level or use the `optional()` wrapper:
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
import { transform, toInt, optional } from '@fishka/express';
|
|
111
|
-
|
|
112
|
-
routes.get('users', async ctx => {
|
|
113
|
-
// Using optional() wrapper for parameters with default values
|
|
114
|
-
const page = ctx.query('page', optional(transform(toInt()))) ?? 1;
|
|
115
|
-
// All parameters are required by default
|
|
116
|
-
const search = ctx.query('search');
|
|
201
|
+
### Parameter Requirements
|
|
117
202
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
203
|
+
- `pathParam(req, 'name')` - returns string (throws 400 if missing)
|
|
204
|
+
- `queryParam(req, 'name')` - returns string (throws 400 if missing/empty)
|
|
205
|
+
- `queryParam(req, 'name', validator)` - returns validated value (throws 400 if missing/empty/invalid)
|
|
206
|
+
- All parameters are required - missing or empty values throw BAD_REQUEST
|
|
121
207
|
|
|
122
208
|
## Authentication
|
|
123
209
|
|
|
@@ -128,12 +214,9 @@ const auth = new BasicAuthStrategy(async (user, pass) =>
|
|
|
128
214
|
user === 'admin' && pass === 'secret' ? { id: '1', role: 'admin' } : null,
|
|
129
215
|
);
|
|
130
216
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const user = getAuthUser(ctx);
|
|
135
|
-
return { id: user.id };
|
|
136
|
-
},
|
|
217
|
+
app.get('/profile', createAuthMiddleware(auth), async (req, res) => {
|
|
218
|
+
const user = getAuthUser(req);
|
|
219
|
+
res.json({ id: user.id });
|
|
137
220
|
});
|
|
138
221
|
```
|
|
139
222
|
|
|
@@ -150,91 +233,129 @@ app.use(
|
|
|
150
233
|
);
|
|
151
234
|
```
|
|
152
235
|
|
|
153
|
-
## HTTP
|
|
236
|
+
## HTTP Errors
|
|
154
237
|
|
|
155
|
-
|
|
238
|
+
Use `HttpError` for specific status codes:
|
|
156
239
|
|
|
157
240
|
```typescript
|
|
158
|
-
import {
|
|
241
|
+
import { HttpError, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND } from '@fishka/express';
|
|
159
242
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
assertHttp(user, HTTP_NOT_FOUND, 'User not found');
|
|
163
|
-
assertHttp(user.isAdmin, HTTP_FORBIDDEN, 'Admin access required');
|
|
164
|
-
```
|
|
243
|
+
app.get('/users/:id', async (req, res) => {
|
|
244
|
+
const user = await db.users.findById(req.params.id);
|
|
165
245
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
246
|
+
if (!req.headers.authorization) {
|
|
247
|
+
throw new HttpError(HTTP_UNAUTHORIZED, 'Authorization required');
|
|
248
|
+
}
|
|
169
249
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
250
|
+
if (!user) {
|
|
251
|
+
throw new HttpError(HTTP_NOT_FOUND, 'User not found');
|
|
252
|
+
}
|
|
173
253
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
app.use(express.json());
|
|
178
|
-
|
|
179
|
-
// 2. Initialize TLS context (Request IDs, etc.)
|
|
180
|
-
// Note: Request ID functionality is disabled by default.
|
|
181
|
-
// To enable it, call configureExpressApi({ requestIdHeader: 'x-request-id' }) first.
|
|
182
|
-
app.use(createTlsMiddleware());
|
|
254
|
+
res.json(user);
|
|
255
|
+
});
|
|
256
|
+
```
|
|
183
257
|
|
|
184
|
-
|
|
185
|
-
const routes = new RouteTable(app);
|
|
258
|
+
Use `assertHttp` for inline assertions:
|
|
186
259
|
|
|
187
|
-
|
|
260
|
+
```typescript
|
|
261
|
+
import { assertHttp, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND } from '@fishka/express';
|
|
188
262
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}));
|
|
263
|
+
app.get('/admin', async (req, res) => {
|
|
264
|
+
assertHttp(req.headers.authorization, HTTP_UNAUTHORIZED, 'Authorization required');
|
|
192
265
|
|
|
193
|
-
|
|
194
|
-
|
|
266
|
+
const user = await db.users.findById(req.params.id);
|
|
267
|
+
assertHttp(user, HTTP_NOT_FOUND, 'User not found');
|
|
268
|
+
assertHttp(user.isAdmin, HTTP_FORBIDDEN, 'Admin access required');
|
|
195
269
|
|
|
196
|
-
|
|
270
|
+
res.json({ admin: true });
|
|
271
|
+
});
|
|
197
272
|
```
|
|
198
273
|
|
|
199
274
|
## Configuration
|
|
200
275
|
|
|
201
|
-
|
|
276
|
+
### Request ID
|
|
202
277
|
|
|
203
278
|
```typescript
|
|
204
|
-
import { configureExpressApi } from '@fishka/express';
|
|
279
|
+
import { configureExpressApi, createTlsMiddleware } from '@fishka/express';
|
|
205
280
|
|
|
206
|
-
//
|
|
281
|
+
// Enable request ID with custom header name
|
|
207
282
|
configureExpressApi({
|
|
208
|
-
// Enable request ID with custom header name
|
|
209
283
|
requestIdHeader: 'x-request-id', // or 'x-correlation-id', 'trace-id', etc.
|
|
210
|
-
|
|
211
|
-
// Whether to trust request ID from client headers
|
|
212
|
-
trustRequestIdHeader: true, // default: true
|
|
284
|
+
trustRequestIdHeader: true, // default: true, if we trust and propagate request ID passed by client.
|
|
213
285
|
});
|
|
214
286
|
|
|
215
|
-
//
|
|
216
|
-
|
|
287
|
+
// Add TLS middleware to track request context
|
|
288
|
+
app.use(createTlsMiddleware());
|
|
217
289
|
```
|
|
218
290
|
|
|
219
|
-
|
|
291
|
+
### Process Handlers
|
|
220
292
|
|
|
221
|
-
Handle uncaught errors and graceful shutdown
|
|
293
|
+
Handle uncaught errors and graceful shutdown:
|
|
222
294
|
|
|
223
295
|
```typescript
|
|
224
296
|
import { installProcessHandlers } from '@fishka/express';
|
|
225
297
|
|
|
226
298
|
installProcessHandlers({
|
|
227
|
-
// Error handlers
|
|
228
299
|
onUncaughtException: err => sendToMonitoring(err),
|
|
229
300
|
onUnhandledRejection: reason => sendToMonitoring(reason),
|
|
230
|
-
|
|
231
|
-
// Graceful shutdown
|
|
232
301
|
onShutdown: async () => {
|
|
233
302
|
await database.close();
|
|
234
303
|
await server.close();
|
|
235
304
|
},
|
|
236
|
-
shutdownTimeout: 15000,
|
|
305
|
+
shutdownTimeout: 15000,
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Complete Example
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import express from 'express';
|
|
313
|
+
import {
|
|
314
|
+
addErrorHandling,
|
|
315
|
+
body,
|
|
316
|
+
min,
|
|
317
|
+
patchExpressAsyncErrors,
|
|
318
|
+
pathParam,
|
|
319
|
+
queryParam,
|
|
320
|
+
range,
|
|
321
|
+
toInt,
|
|
322
|
+
transform,
|
|
323
|
+
} from '@fishka/express';
|
|
324
|
+
import { assertString } from '@fishka/assertions';
|
|
325
|
+
import { assertHttp } from './api.types';
|
|
326
|
+
|
|
327
|
+
const app = express();
|
|
328
|
+
app.use(express.json());
|
|
329
|
+
|
|
330
|
+
// Enable automatic async error catching
|
|
331
|
+
patchExpressAsyncErrors();
|
|
332
|
+
|
|
333
|
+
// Routes
|
|
334
|
+
app.get('/users/:id', async (req, res) => {
|
|
335
|
+
const id = pathParam(req, 'id', transform(toInt()));
|
|
336
|
+
const user = await db.users.findById(id);
|
|
337
|
+
assertHttp(user, 404, 'User not found');
|
|
338
|
+
res.json(user);
|
|
237
339
|
});
|
|
340
|
+
|
|
341
|
+
app.get('/users', async (req, res) => {
|
|
342
|
+
const page = queryParam(req, 'page', transform(toInt(), min(1)));
|
|
343
|
+
const limit = queryParam(req, 'limit', transform(toInt(), range(1, 100)));
|
|
344
|
+
|
|
345
|
+
const users = await db.users.findAll({ page, limit });
|
|
346
|
+
res.json({ users, page, limit });
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
app.post('/users', async (req, res) => {
|
|
350
|
+
const data = body(req, { name: assertString });
|
|
351
|
+
const user = await db.users.create(data);
|
|
352
|
+
res.status(201).json(user);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Error handling (must be last)
|
|
356
|
+
addErrorHandling(app);
|
|
357
|
+
|
|
358
|
+
app.listen(3000);
|
|
238
359
|
```
|
|
239
360
|
|
|
240
361
|
## License
|
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ExpressRequest } from '../utils/express.utils';
|
|
2
2
|
import { AuthStrategy, AuthUser } from './auth.types';
|
|
3
|
+
declare const AUTH_USER_KEY: unique symbol;
|
|
4
|
+
declare module 'express-serve-static-core' {
|
|
5
|
+
interface Request {
|
|
6
|
+
[AUTH_USER_KEY]?: AuthUser;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
3
9
|
/**
|
|
4
|
-
* Creates
|
|
5
|
-
* The authenticated user is stored in the
|
|
10
|
+
* Creates an Express middleware that enforces authentication using the provided strategy.
|
|
11
|
+
* The authenticated user is stored in the request object.
|
|
6
12
|
*
|
|
13
|
+
* @template Credentials - Type of the extracted credentials
|
|
7
14
|
* @template User - Type of the authenticated user
|
|
8
15
|
* @param strategy - Authentication strategy to use
|
|
9
16
|
* @param onSuccess - Optional callback to process authenticated user
|
|
10
|
-
* @returns
|
|
17
|
+
* @returns Express middleware that enforces authentication
|
|
11
18
|
*/
|
|
12
|
-
export declare function createAuthMiddleware<User extends AuthUser = AuthUser>(strategy: AuthStrategy<
|
|
19
|
+
export declare function createAuthMiddleware<Credentials = unknown, User extends AuthUser = AuthUser>(strategy: AuthStrategy<Credentials, User>, onSuccess?: (user: User, req: ExpressRequest) => void): (req: ExpressRequest, res: unknown, next: (err?: unknown) => void) => Promise<void>;
|
|
13
20
|
/**
|
|
14
|
-
* Extracts the authenticated user from the request
|
|
21
|
+
* Extracts the authenticated user from the request.
|
|
15
22
|
* Throws if the user is not present (i.e., authentication was not performed).
|
|
16
23
|
*
|
|
17
24
|
* @template User - Type of the authenticated user
|
|
18
|
-
* @param
|
|
25
|
+
* @param req - Express Request object
|
|
19
26
|
* @returns The authenticated user
|
|
20
|
-
* @throws Error if user is not found in
|
|
27
|
+
* @throws Error if user is not found in request
|
|
21
28
|
*/
|
|
22
|
-
export declare function getAuthUser<User extends AuthUser = AuthUser>(
|
|
29
|
+
export declare function getAuthUser<User extends AuthUser = AuthUser>(req: ExpressRequest): User;
|
|
23
30
|
/**
|
|
24
|
-
* Safely extracts the authenticated user from the request
|
|
31
|
+
* Safely extracts the authenticated user from the request.
|
|
25
32
|
* Returns undefined if the user is not present.
|
|
26
33
|
*
|
|
27
34
|
* @template User - Type of the authenticated user
|
|
28
|
-
* @param
|
|
35
|
+
* @param req - Express Request object
|
|
29
36
|
* @returns The authenticated user, or undefined if not found
|
|
30
37
|
*/
|
|
31
|
-
export declare function tryGetAuthUser<User extends AuthUser = AuthUser>(
|
|
38
|
+
export declare function tryGetAuthUser<User extends AuthUser = AuthUser>(req: ExpressRequest): User | undefined;
|
|
39
|
+
export {};
|
|
@@ -5,61 +5,67 @@ exports.getAuthUser = getAuthUser;
|
|
|
5
5
|
exports.tryGetAuthUser = tryGetAuthUser;
|
|
6
6
|
const api_types_1 = require("../api.types");
|
|
7
7
|
const http_status_codes_1 = require("../http-status-codes");
|
|
8
|
+
// Symbol key for storing auth user in request
|
|
9
|
+
const AUTH_USER_KEY = Symbol('authUser');
|
|
8
10
|
/**
|
|
9
|
-
* Creates
|
|
10
|
-
* The authenticated user is stored in the
|
|
11
|
+
* Creates an Express middleware that enforces authentication using the provided strategy.
|
|
12
|
+
* The authenticated user is stored in the request object.
|
|
11
13
|
*
|
|
14
|
+
* @template Credentials - Type of the extracted credentials
|
|
12
15
|
* @template User - Type of the authenticated user
|
|
13
16
|
* @param strategy - Authentication strategy to use
|
|
14
17
|
* @param onSuccess - Optional callback to process authenticated user
|
|
15
|
-
* @returns
|
|
18
|
+
* @returns Express middleware that enforces authentication
|
|
16
19
|
*/
|
|
17
20
|
function createAuthMiddleware(strategy, onSuccess) {
|
|
18
|
-
return async (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
return async (req, _res, next) => {
|
|
22
|
+
try {
|
|
23
|
+
// Extract credentials from request
|
|
24
|
+
const credentials = strategy.extractCredentials(req);
|
|
25
|
+
// If no credentials found (and strategy returned undefined), we must deny access here.
|
|
26
|
+
if (!credentials) {
|
|
27
|
+
throw new api_types_1.HttpError(http_status_codes_1.HTTP_UNAUTHORIZED, 'No credentials provided or invalid format');
|
|
28
|
+
}
|
|
29
|
+
// Validate credentials and get authenticated user
|
|
30
|
+
const user = await strategy.validateCredentials(credentials);
|
|
31
|
+
// Store authenticated user in request for the handler to access
|
|
32
|
+
req[AUTH_USER_KEY] = user;
|
|
33
|
+
// Optional: Call success callback
|
|
34
|
+
if (onSuccess) {
|
|
35
|
+
onSuccess(user, req);
|
|
36
|
+
}
|
|
37
|
+
next();
|
|
25
38
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// Store authenticated user in state for the handler to access
|
|
29
|
-
context.authUser = user;
|
|
30
|
-
// Optional: Call success callback
|
|
31
|
-
if (onSuccess) {
|
|
32
|
-
onSuccess(user, context);
|
|
39
|
+
catch (error) {
|
|
40
|
+
next(error);
|
|
33
41
|
}
|
|
34
|
-
// Execute the actual handler
|
|
35
|
-
return handler();
|
|
36
42
|
};
|
|
37
43
|
}
|
|
38
44
|
/**
|
|
39
|
-
* Extracts the authenticated user from the request
|
|
45
|
+
* Extracts the authenticated user from the request.
|
|
40
46
|
* Throws if the user is not present (i.e., authentication was not performed).
|
|
41
47
|
*
|
|
42
48
|
* @template User - Type of the authenticated user
|
|
43
|
-
* @param
|
|
49
|
+
* @param req - Express Request object
|
|
44
50
|
* @returns The authenticated user
|
|
45
|
-
* @throws Error if user is not found in
|
|
51
|
+
* @throws Error if user is not found in request
|
|
46
52
|
*/
|
|
47
|
-
function getAuthUser(
|
|
48
|
-
const user =
|
|
53
|
+
function getAuthUser(req) {
|
|
54
|
+
const user = req[AUTH_USER_KEY];
|
|
49
55
|
if (!user) {
|
|
50
|
-
throw new api_types_1.HttpError(http_status_codes_1.HTTP_UNAUTHORIZED, 'User not found in
|
|
56
|
+
throw new api_types_1.HttpError(http_status_codes_1.HTTP_UNAUTHORIZED, 'User not found in request. Did you add auth middleware?');
|
|
51
57
|
}
|
|
52
58
|
return user;
|
|
53
59
|
}
|
|
54
60
|
/**
|
|
55
|
-
* Safely extracts the authenticated user from the request
|
|
61
|
+
* Safely extracts the authenticated user from the request.
|
|
56
62
|
* Returns undefined if the user is not present.
|
|
57
63
|
*
|
|
58
64
|
* @template User - Type of the authenticated user
|
|
59
|
-
* @param
|
|
65
|
+
* @param req - Express Request object
|
|
60
66
|
* @returns The authenticated user, or undefined if not found
|
|
61
67
|
*/
|
|
62
|
-
function tryGetAuthUser(
|
|
63
|
-
return
|
|
68
|
+
function tryGetAuthUser(req) {
|
|
69
|
+
return req[AUTH_USER_KEY];
|
|
64
70
|
}
|
|
65
71
|
//# sourceMappingURL=auth.utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.utils.js","sourceRoot":"","sources":["../../../src/auth/auth.utils.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"auth.utils.js","sourceRoot":"","sources":["../../../src/auth/auth.utils.ts"],"names":[],"mappings":";;AA0BA,oDA8BC;AAWD,kCAMC;AAUD,wCAEC;AArFD,4CAAyC;AACzC,4DAAyD;AAIzD,8CAA8C;AAC9C,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAUzC;;;;;;;;;GASG;AACH,SAAgB,oBAAoB,CAClC,QAAyC,EACzC,SAAqD;IAErD,OAAO,KAAK,EAAE,GAAmB,EAAE,IAAa,EAAE,IAA6B,EAAE,EAAE;QACjF,IAAI,CAAC;YACH,mCAAmC;YACnC,MAAM,WAAW,GAAG,QAAQ,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;YAErD,uFAAuF;YACvF,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,IAAI,qBAAS,CAAC,qCAAiB,EAAE,2CAA2C,CAAC,CAAC;YACtF,CAAC;YAED,kDAAkD;YAClD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,WAA0B,CAAC,CAAC;YAE5E,gEAAgE;YAC/D,GAAmD,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;YAE3E,kCAAkC;YAClC,IAAI,SAAS,EAAE,CAAC;gBACd,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACvB,CAAC;YAED,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,WAAW,CAAmC,GAAmB;IAC/E,MAAM,IAAI,GAAI,GAAmD,CAAC,aAAa,CAAC,CAAC;IACjF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,qBAAS,CAAC,qCAAiB,EAAE,yDAAyD,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,cAAc,CAAmC,GAAmB;IAClF,OAAQ,GAAmD,CAAC,aAAa,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -37,6 +37,27 @@ export declare function catchRouteErrors(fn: ExpressFunction): ExpressFunction;
|
|
|
37
37
|
* so this middleware primarily catches middleware and parsing errors.
|
|
38
38
|
*/
|
|
39
39
|
export declare function catchAllMiddleware(error: unknown, _: ExpressRequest, res: ExpressResponse, next: NextFunction): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Patches Express to automatically catch errors from async route handlers.
|
|
42
|
+
* Call this once at application startup, before defining routes.
|
|
43
|
+
*
|
|
44
|
+
* After patching, you can use async handlers without try/catch:
|
|
45
|
+
* @example
|
|
46
|
+
* patchExpressAsyncErrors();
|
|
47
|
+
*
|
|
48
|
+
* const app = express();
|
|
49
|
+
*
|
|
50
|
+
* app.get('/users', async (req, res) => {
|
|
51
|
+
* const user = await getUser(); // если упадёт - catchAllMiddleware поймает
|
|
52
|
+
* res.json(user);
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* app.use(catchAllMiddleware);
|
|
56
|
+
*
|
|
57
|
+
* Note: This patches Express internal API. Works with Express 4.x and 5.x.
|
|
58
|
+
* Safe to call multiple times - patch applies only once.
|
|
59
|
+
*/
|
|
60
|
+
export declare function patchExpressAsyncErrors(): void;
|
|
40
61
|
/** Options for installProcessHandlers(). */
|
|
41
62
|
export interface ProcessHandlersOptions {
|
|
42
63
|
/** Custom handler for uncaught exceptions. Called before default logging. */
|