@fishka/express 0.9.24 → 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.
Files changed (44) hide show
  1. package/README.md +227 -106
  2. package/dist/cjs/auth/auth.utils.d.ts +20 -12
  3. package/dist/cjs/auth/auth.utils.js +35 -29
  4. package/dist/cjs/auth/auth.utils.js.map +1 -1
  5. package/dist/cjs/error-handling.d.ts +21 -0
  6. package/dist/cjs/error-handling.js +64 -0
  7. package/dist/cjs/error-handling.js.map +1 -1
  8. package/dist/cjs/index.d.ts +1 -2
  9. package/dist/cjs/index.js +1 -2
  10. package/dist/cjs/index.js.map +1 -1
  11. package/dist/cjs/request-utils.d.ts +29 -0
  12. package/dist/cjs/request-utils.js +115 -0
  13. package/dist/cjs/request-utils.js.map +1 -0
  14. package/dist/cjs/thread-local/thread-local-storage-middleware.d.ts +1 -1
  15. package/dist/cjs/thread-local/thread-local-storage-middleware.js +14 -2
  16. package/dist/cjs/thread-local/thread-local-storage-middleware.js.map +1 -1
  17. package/dist/esm/auth/auth.utils.d.ts +20 -12
  18. package/dist/esm/auth/auth.utils.js +35 -29
  19. package/dist/esm/auth/auth.utils.js.map +1 -1
  20. package/dist/esm/error-handling.d.ts +21 -0
  21. package/dist/esm/error-handling.js +63 -0
  22. package/dist/esm/error-handling.js.map +1 -1
  23. package/dist/esm/index.d.ts +1 -2
  24. package/dist/esm/index.js +1 -2
  25. package/dist/esm/index.js.map +1 -1
  26. package/dist/esm/request-utils.d.ts +29 -0
  27. package/dist/esm/request-utils.js +77 -0
  28. package/dist/esm/request-utils.js.map +1 -0
  29. package/dist/esm/thread-local/thread-local-storage-middleware.d.ts +1 -1
  30. package/dist/esm/thread-local/thread-local-storage-middleware.js +15 -3
  31. package/dist/esm/thread-local/thread-local-storage-middleware.js.map +1 -1
  32. package/package.json +1 -1
  33. package/dist/cjs/route-table.d.ts +0 -30
  34. package/dist/cjs/route-table.js +0 -40
  35. package/dist/cjs/route-table.js.map +0 -1
  36. package/dist/cjs/router.d.ts +0 -86
  37. package/dist/cjs/router.js +0 -192
  38. package/dist/cjs/router.js.map +0 -1
  39. package/dist/esm/route-table.d.ts +0 -30
  40. package/dist/esm/route-table.js +0 -36
  41. package/dist/esm/route-table.js.map +0 -1
  42. package/dist/esm/router.d.ts +0 -86
  43. package/dist/esm/router.js +0 -156
  44. package/dist/esm/router.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Express API
2
2
 
3
- Type-safe Express.js routing with clean, minimal API.
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 { RouteTable, transform, toInt } from '@fishka/express';
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
- const routes = new RouteTable(app);
25
+ // Enable automatic async error catching (call once at startup)
26
+ patchExpressAsyncErrors();
22
27
 
23
28
  // GET /users/:id - with typed path params
24
- routes.get('users/:id', async ctx => ({
25
- id: ctx.path('id', transform(toInt())), // number - validated inline
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 - list all users
30
- routes.get('users', async () => [
31
- { id: 1, name: 'John' },
32
- { id: 2, name: 'Jane' },
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
- routes.post('users', async ctx => ({
37
- id: 1,
38
- name: ctx.body({ name: v => assertString(v, 'name required') }).name,
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
- // DELETE /users/:id
42
- routes.delete('users/:id', async () => {});
46
+ // Add error handling middleware (must be last)
47
+ addErrorHandling(app);
43
48
 
44
49
  app.listen(3000);
45
50
  ```
46
51
 
47
- ## URL Parameter Validation
52
+ ## Utilities
53
+
54
+ ### `pathParam(req, name, validator?)`
48
55
 
49
- Use `transform()` to validate and transform path/query parameters. All operators are composable:
56
+ Get and validate a path parameter from Express request.
50
57
 
51
58
  ```typescript
52
- import { transform, toInt, minLength, matches, min, range, oneOf } from '@fishka/express';
59
+ import { pathParam, transform, toInt, minLength } from '@fishka/express';
53
60
 
54
- routes.get('users/:id', async ctx => ({
55
- id: ctx.path('id', transform(toInt())), // string number (required)
56
- page: ctx.query('page', transform(toInt(), min(1))), // number >= 1, required (throws 400 if missing)
57
- limit: ctx.query('limit', transform(toInt(), range(1, 100))), // number 1-100, required (throws 400 if missing)
58
- sort: ctx.query('sort', transform(oneOf('asc', 'desc'))), // enum, required (throws 400 if missing)
59
- search: ctx.query('search', transform(minLength(3))), // string min 3 chars, required (throws 400 if missing)
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
- ### Parameter Requirements
75
+ ### `queryParam(req, name, validator?)`
64
76
 
65
- - `ctx.path('name')` - returns string (throws 400 if missing)
66
- - `ctx.query('name')` - returns string (throws 400 if missing/empty)
67
- - `ctx.query('name', validator)` - returns validated value (throws 400 if missing/empty/invalid)
68
- - All parameters are required - missing or empty values throw BAD_REQUEST
69
- - Validators receive raw values (including undefined/null/empty) and can enforce additional validation
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
- ### All Parameters Are Required
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
- return { page: page ?? 1, search };
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
- routes.get('profile', {
132
- middlewares: [createAuthMiddleware(auth)],
133
- run: async ctx => {
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 Status Code in Validation
236
+ ## HTTP Errors
154
237
 
155
- For cases where you need specific HTTP status codes (like 401 for authentication, 404 for not found), use `assertHttp`:
238
+ Use `HttpError` for specific status codes:
156
239
 
157
240
  ```typescript
158
- import { assertHttp, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND } from '@fishka/express';
241
+ import { HttpError, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND } from '@fishka/express';
159
242
 
160
- // In a validator or route handler
161
- assertHttp(req.headers.authorization, HTTP_UNAUTHORIZED, 'Authorization required');
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
- ## Complete Example
167
-
168
- Full initialization with TLS context, validation, and error handling:
246
+ if (!req.headers.authorization) {
247
+ throw new HttpError(HTTP_UNAUTHORIZED, 'Authorization required');
248
+ }
169
249
 
170
- ```typescript
171
- import express from 'express';
172
- import { RouteTable, createTlsMiddleware, catchAllMiddleware, transform, toInt } from '@fishka/express';
250
+ if (!user) {
251
+ throw new HttpError(HTTP_NOT_FOUND, 'User not found');
252
+ }
173
253
 
174
- const app = express();
175
-
176
- // 1. Basic express middleware
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
- // 3. Define routes with typed parameters
185
- const routes = new RouteTable(app);
258
+ Use `assertHttp` for inline assertions:
186
259
 
187
- routes.get('health', async () => ({ status: 'UP' }));
260
+ ```typescript
261
+ import { assertHttp, HTTP_UNAUTHORIZED, HTTP_NOT_FOUND } from '@fishka/express';
188
262
 
189
- routes.get('users/:id', async ctx => ({
190
- id: ctx.path('id', transform(toInt())),
191
- }));
263
+ app.get('/admin', async (req, res) => {
264
+ assertHttp(req.headers.authorization, HTTP_UNAUTHORIZED, 'Authorization required');
192
265
 
193
- // 4. Error handler - catches middleware/parsing errors
194
- app.use(catchAllMiddleware);
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
- app.listen(3000);
270
+ res.json({ admin: true });
271
+ });
197
272
  ```
198
273
 
199
274
  ## Configuration
200
275
 
201
- You can configure global settings using `configureExpressApi`:
276
+ ### Request ID
202
277
 
203
278
  ```typescript
204
- import { configureExpressApi } from '@fishka/express';
279
+ import { configureExpressApi, createTlsMiddleware } from '@fishka/express';
205
280
 
206
- // Request ID configuration (disabled by default)
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
- // By default, request ID functionality is disabled.
216
- // To enable it, you must set requestIdHeader.
287
+ // Add TLS middleware to track request context
288
+ app.use(createTlsMiddleware());
217
289
  ```
218
290
 
219
- ## Process Handlers
291
+ ### Process Handlers
220
292
 
221
- Handle uncaught errors and graceful shutdown in one place:
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, // Force exit after 15s (default: 10s)
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 { EndpointMiddleware, RequestContext } from '../router';
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 a middleware that enforces authentication using the provided strategy.
5
- * The authenticated user is stored in the context under the 'authUser' key.
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 a middleware that enforces authentication
17
+ * @returns Express middleware that enforces authentication
11
18
  */
12
- export declare function createAuthMiddleware<User extends AuthUser = AuthUser>(strategy: AuthStrategy<unknown, User>, onSuccess?: (user: User, context: RequestContext) => void): EndpointMiddleware;
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 context.
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 context - Request context
25
+ * @param req - Express Request object
19
26
  * @returns The authenticated user
20
- * @throws Error if user is not found in context
27
+ * @throws Error if user is not found in request
21
28
  */
22
- export declare function getAuthUser<User extends AuthUser = AuthUser>(context: RequestContext): User;
29
+ export declare function getAuthUser<User extends AuthUser = AuthUser>(req: ExpressRequest): User;
23
30
  /**
24
- * Safely extracts the authenticated user from the request context.
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 context - Request context
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>(context: RequestContext): User | undefined;
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 a middleware that enforces authentication using the provided strategy.
10
- * The authenticated user is stored in the context under the 'authUser' key.
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 a middleware that enforces authentication
18
+ * @returns Express middleware that enforces authentication
16
19
  */
17
20
  function createAuthMiddleware(strategy, onSuccess) {
18
- return async (handler, context) => {
19
- // Extract credentials from request
20
- const credentials = strategy.extractCredentials(context.req);
21
- // If no credentials found (and strategy returned undefined), we must deny access here.
22
- // In a composite strategy scenario, we might want to try the next strategy, but this helper is for a single strategy enforcement.
23
- if (!credentials) {
24
- throw new api_types_1.HttpError(http_status_codes_1.HTTP_UNAUTHORIZED, 'No credentials provided or invalid format');
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
- // Validate credentials and get authenticated user
27
- const user = await strategy.validateCredentials(credentials);
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 context.
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 context - Request context
49
+ * @param req - Express Request object
44
50
  * @returns The authenticated user
45
- * @throws Error if user is not found in context
51
+ * @throws Error if user is not found in request
46
52
  */
47
- function getAuthUser(context) {
48
- const user = context.authUser;
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 context. Did you add auth middleware?');
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 context.
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 context - Request context
65
+ * @param req - Express Request object
60
66
  * @returns The authenticated user, or undefined if not found
61
67
  */
62
- function tryGetAuthUser(context) {
63
- return context.authUser;
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":";;AAcA,oDA4BC;AAWD,kCAMC;AAUD,wCAEC;AAvED,4CAAyC;AACzC,4DAAyD;AAIzD;;;;;;;;GAQG;AACH,SAAgB,oBAAoB,CAClC,QAAqC,EACrC,SAAyD;IAEzD,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QAChC,mCAAmC;QACnC,MAAM,WAAW,GAAG,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAE7D,uFAAuF;QACvF,kIAAkI;QAClI,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,qBAAS,CAAC,qCAAiB,EAAE,2CAA2C,CAAC,CAAC;QACtF,CAAC;QAED,kDAAkD;QAClD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAE7D,8DAA8D;QAC9D,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;QAExB,kCAAkC;QAClC,IAAI,SAAS,EAAE,CAAC;YACd,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC3B,CAAC;QAED,6BAA6B;QAC7B,OAAO,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,WAAW,CAAmC,OAAuB;IACnF,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,qBAAS,CAAC,qCAAiB,EAAE,yDAAyD,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,IAAY,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,cAAc,CAAmC,OAAuB;IACtF,OAAO,OAAO,CAAC,QAA4B,CAAC;AAC9C,CAAC"}
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. */