exisjs 0.7.4 → 0.7.6

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 (197) hide show
  1. package/dist/cli/commands/build.d.ts +4 -2
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +80 -22
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/dev.d.ts.map +1 -1
  6. package/dist/cli/commands/dev.js +229 -64
  7. package/dist/cli/commands/dev.js.map +1 -1
  8. package/dist/cli/commands/doctor.d.ts.map +1 -1
  9. package/dist/cli/commands/doctor.js +5 -2
  10. package/dist/cli/commands/doctor.js.map +1 -1
  11. package/dist/cli/commands/generate.d.ts +1 -0
  12. package/dist/cli/commands/generate.d.ts.map +1 -1
  13. package/dist/cli/commands/generate.js +25 -0
  14. package/dist/cli/commands/generate.js.map +1 -1
  15. package/dist/cli/commands/start.d.ts.map +1 -1
  16. package/dist/cli/commands/start.js +65 -15
  17. package/dist/cli/commands/start.js.map +1 -1
  18. package/dist/cli/index.js +20 -4
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/manifest.d.ts.map +1 -1
  21. package/dist/cli/manifest.js +118 -17
  22. package/dist/cli/manifest.js.map +1 -1
  23. package/dist/cli/resolve-aliases.d.ts +1 -1
  24. package/dist/cli/resolve-aliases.d.ts.map +1 -1
  25. package/dist/cli/resolve-aliases.js +30 -13
  26. package/dist/cli/resolve-aliases.js.map +1 -1
  27. package/dist/config/config.d.ts +3 -1
  28. package/dist/config/config.d.ts.map +1 -1
  29. package/dist/config/config.js.map +1 -1
  30. package/dist/config/env.d.ts.map +1 -1
  31. package/dist/config/env.js +8 -0
  32. package/dist/config/env.js.map +1 -1
  33. package/dist/config/types.d.ts +2 -0
  34. package/dist/config/types.d.ts.map +1 -1
  35. package/dist/decorators/constants.d.ts +2 -0
  36. package/dist/decorators/constants.d.ts.map +1 -1
  37. package/dist/decorators/middleware.d.ts +65 -2
  38. package/dist/decorators/middleware.d.ts.map +1 -1
  39. package/dist/decorators/middleware.js +148 -62
  40. package/dist/decorators/middleware.js.map +1 -1
  41. package/dist/decorators/params.d.ts +5 -0
  42. package/dist/decorators/params.d.ts.map +1 -1
  43. package/dist/decorators/params.js +11 -1
  44. package/dist/decorators/params.js.map +1 -1
  45. package/dist/decorators/response.d.ts.map +1 -1
  46. package/dist/decorators/response.js +32 -22
  47. package/dist/decorators/response.js.map +1 -1
  48. package/dist/di/container.d.ts.map +1 -1
  49. package/dist/di/container.js +6 -1
  50. package/dist/di/container.js.map +1 -1
  51. package/dist/docs/content/boundaries.mdx +33 -3
  52. package/dist/docs/content/cli/commands.mdx +1 -1
  53. package/dist/docs/content/config.mdx +26 -0
  54. package/dist/docs/content/controllers.mdx +127 -10
  55. package/dist/docs/content/decorators.mdx +22 -4
  56. package/dist/docs/content/errors.mdx +38 -2
  57. package/dist/docs/content/introduction.mdx +7 -7
  58. package/dist/docs/content/middleware.mdx +269 -6
  59. package/dist/docs/content/requests.mdx +217 -10
  60. package/dist/docs/content/responses.mdx +323 -34
  61. package/dist/docs/content/routing.mdx +46 -0
  62. package/dist/docs/content/security.mdx +17 -4
  63. package/dist/docs/content/structure.mdx +91 -12
  64. package/dist/docs/content/validation.mdx +226 -11
  65. package/dist/docs/reference/context.mdx +11 -0
  66. package/dist/docs/reference/decorators.mdx +14 -4
  67. package/dist/docs/reference/error.mdx +44 -0
  68. package/dist/docs/reference/logger.mdx +175 -43
  69. package/dist/docs/reference/middleware.mdx +31 -0
  70. package/dist/docs/reference/router.mdx +22 -0
  71. package/dist/docs/reference/validator.mdx +83 -23
  72. package/dist/error/errors.d.ts.map +1 -1
  73. package/dist/error/errors.js +95 -43
  74. package/dist/error/errors.js.map +1 -1
  75. package/dist/error/index.d.ts +1 -0
  76. package/dist/error/index.d.ts.map +1 -1
  77. package/dist/error/index.js +11 -1
  78. package/dist/error/index.js.map +1 -1
  79. package/dist/error/overlay.d.ts +42 -0
  80. package/dist/error/overlay.d.ts.map +1 -0
  81. package/dist/error/overlay.js +306 -0
  82. package/dist/error/overlay.js.map +1 -0
  83. package/dist/index.d.ts +1 -0
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +22 -1
  86. package/dist/index.js.map +1 -1
  87. package/dist/lib/start-server.js +49 -7
  88. package/dist/lib/start-server.js.map +1 -1
  89. package/dist/logger/index.d.ts +1 -1
  90. package/dist/logger/index.d.ts.map +1 -1
  91. package/dist/logger/index.js +9 -1
  92. package/dist/logger/index.js.map +1 -1
  93. package/dist/middleware/guard.d.ts +2 -2
  94. package/dist/middleware/guard.d.ts.map +1 -1
  95. package/dist/middleware/guard.js.map +1 -1
  96. package/dist/middleware/ip-filter.d.ts +1 -0
  97. package/dist/middleware/ip-filter.d.ts.map +1 -1
  98. package/dist/middleware/ip-filter.js +2 -0
  99. package/dist/middleware/ip-filter.js.map +1 -1
  100. package/dist/middleware/middleware.d.ts +2 -2
  101. package/dist/middleware/middleware.d.ts.map +1 -1
  102. package/dist/middleware/middleware.js +20 -2
  103. package/dist/middleware/middleware.js.map +1 -1
  104. package/dist/middleware/rate-limit.d.ts.map +1 -1
  105. package/dist/middleware/rate-limit.js +9 -0
  106. package/dist/middleware/rate-limit.js.map +1 -1
  107. package/dist/middleware/security.d.ts +43 -1
  108. package/dist/middleware/security.d.ts.map +1 -1
  109. package/dist/middleware/security.js +101 -0
  110. package/dist/middleware/security.js.map +1 -1
  111. package/dist/response/index.d.ts +229 -6
  112. package/dist/response/index.d.ts.map +1 -1
  113. package/dist/response/index.js +272 -2
  114. package/dist/response/index.js.map +1 -1
  115. package/dist/router/boundary.d.ts +25 -5
  116. package/dist/router/boundary.d.ts.map +1 -1
  117. package/dist/router/boundary.js.map +1 -1
  118. package/dist/router/controller-registrar.d.ts.map +1 -1
  119. package/dist/router/controller-registrar.js +56 -1
  120. package/dist/router/controller-registrar.js.map +1 -1
  121. package/dist/router/index.d.ts +2 -2
  122. package/dist/router/index.d.ts.map +1 -1
  123. package/dist/router/index.js +2 -1
  124. package/dist/router/index.js.map +1 -1
  125. package/dist/router/route-builder.d.ts +70 -36
  126. package/dist/router/route-builder.d.ts.map +1 -1
  127. package/dist/router/route-builder.js +13 -15
  128. package/dist/router/route-builder.js.map +1 -1
  129. package/dist/router/route-scanner.d.ts +1 -0
  130. package/dist/router/route-scanner.d.ts.map +1 -1
  131. package/dist/router/route-scanner.js +217 -10
  132. package/dist/router/route-scanner.js.map +1 -1
  133. package/dist/router/router.d.ts.map +1 -1
  134. package/dist/router/router.js +35 -17
  135. package/dist/router/router.js.map +1 -1
  136. package/dist/router/types.d.ts +11 -1
  137. package/dist/router/types.d.ts.map +1 -1
  138. package/dist/sanitize/index.d.ts +6 -6
  139. package/dist/sanitize/index.d.ts.map +1 -1
  140. package/dist/sanitize/index.js +8 -8
  141. package/dist/sanitize/index.js.map +1 -1
  142. package/dist/server/app.d.ts +2 -0
  143. package/dist/server/app.d.ts.map +1 -1
  144. package/dist/server/app.js +14 -0
  145. package/dist/server/app.js.map +1 -1
  146. package/dist/server/bootstrapper.d.ts.map +1 -1
  147. package/dist/server/bootstrapper.js +91 -30
  148. package/dist/server/bootstrapper.js.map +1 -1
  149. package/dist/server/cluster.d.ts.map +1 -1
  150. package/dist/server/cluster.js +13 -2
  151. package/dist/server/cluster.js.map +1 -1
  152. package/dist/server/context.d.ts +8 -0
  153. package/dist/server/context.d.ts.map +1 -1
  154. package/dist/server/context.js +50 -6
  155. package/dist/server/context.js.map +1 -1
  156. package/dist/server/engines/node-engine.d.ts.map +1 -1
  157. package/dist/server/engines/node-engine.js +1 -0
  158. package/dist/server/engines/node-engine.js.map +1 -1
  159. package/dist/server/lifecycle.d.ts +22 -0
  160. package/dist/server/lifecycle.d.ts.map +1 -0
  161. package/dist/server/lifecycle.js +106 -0
  162. package/dist/server/lifecycle.js.map +1 -0
  163. package/dist/server/request-handler.d.ts +1 -0
  164. package/dist/server/request-handler.d.ts.map +1 -1
  165. package/dist/server/request-handler.js +42 -18
  166. package/dist/server/request-handler.js.map +1 -1
  167. package/dist/server/request.d.ts +6 -0
  168. package/dist/server/request.d.ts.map +1 -1
  169. package/dist/server/request.js +112 -33
  170. package/dist/server/request.js.map +1 -1
  171. package/dist/server/response.d.ts +13 -2
  172. package/dist/server/response.d.ts.map +1 -1
  173. package/dist/server/response.js +250 -10
  174. package/dist/server/response.js.map +1 -1
  175. package/dist/testing/index.d.ts.map +1 -1
  176. package/dist/testing/index.js +6 -1
  177. package/dist/testing/index.js.map +1 -1
  178. package/dist/testing/mocks.d.ts.map +1 -1
  179. package/dist/testing/mocks.js +15 -3
  180. package/dist/testing/mocks.js.map +1 -1
  181. package/dist/utils/logger.d.ts +8 -0
  182. package/dist/utils/logger.d.ts.map +1 -1
  183. package/dist/utils/logger.js +184 -1
  184. package/dist/utils/logger.js.map +1 -1
  185. package/dist/validator/tex-types.d.ts +2 -1
  186. package/dist/validator/tex-types.d.ts.map +1 -1
  187. package/dist/validator/tex-types.js +2 -1
  188. package/dist/validator/tex-types.js.map +1 -1
  189. package/dist/validator/tex.d.ts +38 -22
  190. package/dist/validator/tex.d.ts.map +1 -1
  191. package/dist/validator/tex.js +490 -40
  192. package/dist/validator/tex.js.map +1 -1
  193. package/package.json +10 -10
  194. package/dist/dev/error-overlay.d.ts +0 -3
  195. package/dist/dev/error-overlay.d.ts.map +0 -1
  196. package/dist/dev/error-overlay.js +0 -118
  197. package/dist/dev/error-overlay.js.map +0 -1
@@ -97,7 +97,9 @@ Handling `multipart/form-data` in Node.js can be difficult. ExisJS provides mult
97
97
 
98
98
  ### A. Declarative Route Uploads (Recommended)
99
99
 
100
- You can declare file upload requirements directly inside your route schema. ExisJS automatically executes the multipart parser, validates MIME types, enforces byte caps, and populates `req.file` or `req.files` along with non-file fields in `req.body`:
100
+ You can declare file upload requirements directly inside your route schema or decorators. ExisJS automatically executes the multipart parser, validates MIME types, enforces byte caps, and populates `req.file` or `req.files` along with non-file fields in `req.body`:
101
+
102
+ #### Functional Paradigm
101
103
 
102
104
  ```typescript filename="src/http/upload/route.ts"
103
105
  import { controller, route } from 'exisjs/router'
@@ -128,10 +130,47 @@ export default controller({
128
130
  })
129
131
  ```
130
132
 
133
+ #### Class-Based (OOP) Paradigm
134
+
135
+ ```typescript filename="src/http/upload/route.ts"
136
+ import { Controller, Post, UseUpload, UploadedFile, Body } from 'exisjs/decorators'
137
+ import { tex } from 'exisjs/validator'
138
+ import type { UploadedFile as IUploadedFile } from 'exisjs/router'
139
+
140
+ const TitleSchema = tex.object({
141
+ title: tex.string({ min: 1 }),
142
+ })
143
+
144
+ @Controller('/upload')
145
+ export default class UploadController {
146
+ @Post('/')
147
+ @UseUpload({
148
+ field: 'photo',
149
+ maxBytes: 10 * 1024 * 1024,
150
+ mimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
151
+ })
152
+ async uploadPhoto(
153
+ @UploadedFile() file: IUploadedFile,
154
+ @Body(TitleSchema) body: { title: string }
155
+ ) {
156
+ console.log(`Uploaded ${file.originalname} (${file.size} bytes)`)
157
+
158
+ return {
159
+ success: true,
160
+ filename: file.filename,
161
+ mimetype: file.mimetype,
162
+ title: body.title,
163
+ }
164
+ }
165
+ }
166
+ ```
167
+
131
168
  ### B. First-Party `fileUpload` Middleware
132
169
 
133
170
  ExisJS exports a dedicated `fileUpload` middleware from `exisjs/middleware` supporting single, multiple, and multi-field uploads:
134
171
 
172
+ #### Functional Paradigm
173
+
135
174
  ```typescript filename="src/http/documents/route.ts"
136
175
  import { controller, route } from 'exisjs/router'
137
176
  import { fileUpload } from 'exisjs/middleware'
@@ -173,10 +212,44 @@ export default controller({
173
212
  })
174
213
  ```
175
214
 
215
+ #### Class-Based (OOP) Paradigm
216
+
217
+ ```typescript filename="src/http/documents/route.ts"
218
+ import { Controller, Post, Use, UploadedFile, UploadedFiles } from 'exisjs/decorators'
219
+ import { fileUpload } from 'exisjs/middleware'
220
+ import type { UploadedFile as IUploadedFile } from 'exisjs/router'
221
+
222
+ @Controller('/documents')
223
+ export default class DocumentsController {
224
+ // Single file upload
225
+ @Post('/single')
226
+ @Use(fileUpload.single('document', { maxBytes: 15 * 1024 * 1024, dest: './uploads/docs' }))
227
+ async uploadSingle(@UploadedFile() file: IUploadedFile) {
228
+ return { file }
229
+ }
230
+
231
+ // Multiple files with the same field name
232
+ @Post('/gallery')
233
+ @Use(fileUpload.array('photos', 5, { mimeTypes: ['image/*'] }))
234
+ async uploadGallery(@UploadedFiles() files: IUploadedFile[]) {
235
+ return { count: files.length, files }
236
+ }
237
+
238
+ // Multiple distinct fields
239
+ @Post('/profile')
240
+ @Use(fileUpload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'cover', maxCount: 1 }]))
241
+ async uploadProfile(@UploadedFiles() files: Record<string, IUploadedFile[]>) {
242
+ return { files }
243
+ }
244
+ }
245
+ ```
246
+
176
247
  ### C. Integrating Express/Connect Middleware (e.g. Multer)
177
248
 
178
249
  If your enterprise project relies on existing Express middleware like **Multer** (for custom storage engines, S3 direct-to-cloud streams, or specialized filters), ExisJS provides the `fromExpress()` bridge:
179
250
 
251
+ #### Functional Paradigm
252
+
180
253
  ```typescript filename="src/http/legacy/route.ts"
181
254
  import { controller, route } from 'exisjs/router'
182
255
  import { fromExpress } from 'exisjs/middleware'
@@ -199,10 +272,36 @@ export default controller({
199
272
  })
200
273
  ```
201
274
 
275
+ #### Class-Based (OOP) Paradigm
276
+
277
+ ```typescript filename="src/http/legacy/route.ts"
278
+ import { Controller, Post, Use, Req } from 'exisjs/decorators'
279
+ import { fromExpress } from 'exisjs/middleware'
280
+ import type { Request } from 'exisjs/router'
281
+ import multer from 'multer'
282
+
283
+ const upload = multer({
284
+ storage: multer.memoryStorage(),
285
+ limits: { fileSize: 10 * 1024 * 1024 },
286
+ })
287
+
288
+ @Controller('/legacy')
289
+ export default class LegacyUploadController {
290
+ @Post('/upload')
291
+ @Use(fromExpress(upload.single('file')))
292
+ async upload(@Req() req: Request) {
293
+ const file = (req.raw as any).file || req.file
294
+ return { success: true, originalname: file?.originalname }
295
+ }
296
+ }
297
+ ```
298
+
202
299
  ### D. Manual `req.formData()` & Streaming Uploads
203
300
 
204
301
  For manual control over incoming multipart streams without route middleware:
205
302
 
303
+ #### Functional Paradigm
304
+
206
305
  ```typescript filename="src/http/upload/route.ts"
207
306
  import { controller, route } from 'exisjs/router'
208
307
 
@@ -225,6 +324,29 @@ export default controller({
225
324
  })
226
325
  ```
227
326
 
327
+ #### Class-Based (OOP) Paradigm
328
+
329
+ ```typescript filename="src/http/upload/route.ts"
330
+ import { Controller, Post, Req } from 'exisjs/decorators'
331
+ import type { Request } from 'exisjs/router'
332
+
333
+ @Controller('/upload')
334
+ export default class ManualUploadController {
335
+ @Post('/avatar')
336
+ async uploadAvatar(@Req() req: Request) {
337
+ const { fields, files } = await req.formData()
338
+ const uploadedFile = files['profile_pic']
339
+
340
+ if (uploadedFile) {
341
+ const savedPath = await uploadedFile.saveToDisk('./uploads/avatars')
342
+ return { success: true, path: savedPath }
343
+ }
344
+
345
+ return { success: false, error: 'No file uploaded' }
346
+ }
347
+ }
348
+ ```
349
+
228
350
  ---
229
351
 
230
352
  ## 4. Body Size Limits & HTTP 413
@@ -260,22 +382,24 @@ ExisJS ships with a **60-second default request timeout** in its `timeout()` mid
260
382
 
261
383
  ### Dynamic In-Handler Control
262
384
 
263
- Handlers can dynamically extend or cancel the active timeout on the fly using `req.setTimeout()` and `req.clearTimeout()`:
385
+ Handlers can dynamically extend or cancel the active timeout on the fly using `req.setTimeout()` and `req.clearTimeout()`, or declaratively via `@Timeout(ms)`:
386
+
387
+ #### Functional Paradigm
264
388
 
265
389
  ```typescript filename="src/http/ai/route.ts"
266
390
  import { controller, route } from 'exisjs/router'
267
391
 
268
392
  export default controller({
269
393
  generateVideo: route.post('/', {
394
+ timeoutMs: 180000, // 1. Declarative 3-minute timeout for this endpoint
270
395
  async handle({ req }) {
271
- // 1. Extend timeout for heavy AI pass generation (e.g. 3 minutes)
272
- req.setTimeout(180000)
396
+ // 2. Or dynamically adjust timeout during processing
397
+ req.setTimeout(240000)
273
398
 
274
399
  try {
275
400
  const result = await longRunningAiTask()
276
401
  return { success: true, result }
277
402
  } finally {
278
- // 2. Clear timeout once work completes
279
403
  req.clearTimeout()
280
404
  }
281
405
  }
@@ -283,6 +407,30 @@ export default controller({
283
407
  })
284
408
  ```
285
409
 
410
+ #### Class-Based (OOP) Paradigm
411
+
412
+ ```typescript filename="src/http/ai/route.ts"
413
+ import { Controller, Post, Timeout, Req } from 'exisjs/decorators'
414
+ import type { Request } from 'exisjs/router'
415
+
416
+ @Controller('/ai')
417
+ export default class AiTaskController {
418
+ @Post('/generate-video')
419
+ @Timeout(180000) // 1. Declarative 3-minute timeout
420
+ async generateVideo(@Req() req: Request) {
421
+ // 2. Or dynamically adjust on the fly
422
+ req.setTimeout(240000)
423
+
424
+ try {
425
+ const result = await longRunningAiTask()
426
+ return { success: true, result }
427
+ } finally {
428
+ req.clearTimeout()
429
+ }
430
+ }
431
+ }
432
+ ```
433
+
286
434
  ---
287
435
 
288
436
  ## 6. HTTP Headers & Content Negotiation
@@ -303,19 +451,78 @@ const language = req.acceptsLanguages('en', 'es', 'fr')
303
451
 
304
452
  ---
305
453
 
306
- ## 5. Network Information
454
+ ## 7. Network Information & IPv6 Normalization
307
455
 
308
- ExisJS handles trust-proxies. If you are behind Cloudflare, Nginx, or an AWS Load Balancer, simply set `trustProxy: true` in your `exis()` config, and these properties will automatically resolve the true client IP from `X-Forwarded-For`.
456
+ ExisJS handles trust-proxies and dual-stack IP networks automatically. If you are behind Cloudflare, Nginx, or an AWS Load Balancer, simply set `trustProxy: true` in your `exis()` config, and these properties will automatically resolve the true client IP from `X-Forwarded-For`.
309
457
 
310
- - **`req.ip`**: The client's IP address.
311
- - **`req.ips`**: An array of IPs if multiple proxies were traversed.
458
+ - **`req.ip`**: The client's IP address. Automatically normalizes IPv6-mapped IPv4 dual-stack addresses (e.g., `::ffff:192.168.1.50` $\to$ `192.168.1.50`) and converts IPv6 loopback `::1` $\to$ `127.0.0.1` for consistent rate limiting and geo-IP lookups.
459
+ - **`req.ips`**: An array of IPs if multiple proxies were traversed, each normalized cleanly.
312
460
  - **`req.hostname`**: The resolved hostname (from `Host` or `X-Forwarded-Host`).
313
461
  - **`req.protocol`**: Returns `'http'` or `'https'`.
314
462
  - **`req.secure`**: A boolean (true if `req.protocol === 'https'`).
315
463
 
316
464
  ---
317
465
 
318
- ## 6. Context & Metadata
466
+ ## 8. Request Cancellation & Abort Signals (`req.signal`)
467
+
468
+ ExisJS provides a first-class standard Web API `req.signal: AbortSignal` tied directly to the underlying HTTP connection lifecycle.
469
+
470
+ When a client closes their browser tab, cancels a download, or aborts an HTTP request, `req.signal` immediately triggers an `abort` event. You can pass `req.signal` directly to ORMs, database drivers, and HTTP clients to cancel expensive in-flight operations and eliminate wasted server resources.
471
+
472
+ #### Functional Paradigm
473
+
474
+ ```typescript filename="src/http/reports/route.ts"
475
+ import { controller, route } from 'exisjs/router'
476
+
477
+ export default controller({
478
+ heavyReport: route.get('/', {
479
+ async handle({ req }) {
480
+ // 1. Pass req.signal to fetch() — if the client leaves, fetch aborts immediately
481
+ const upstream = await fetch('https://api.external.com/data', {
482
+ signal: req.signal
483
+ })
484
+ const payload = await upstream.json()
485
+
486
+ // 2. Pass req.signal to your database queries (Prisma, Drizzle, pg, etc.)
487
+ const results = await db.query(sql`SELECT * FROM heavy_analytics`, {
488
+ signal: req.signal
489
+ })
490
+
491
+ return { payload, results }
492
+ }
493
+ })
494
+ })
495
+ ```
496
+
497
+ #### Class-Based (OOP) Paradigm
498
+
499
+ ```typescript filename="src/http/reports/route.ts"
500
+ import { Controller, Get, Req } from 'exisjs/decorators'
501
+ import type { Request } from 'exisjs/router'
502
+
503
+ @Controller('/reports')
504
+ export default class ReportsController {
505
+ @Get('/')
506
+ async heavyReport(@Req() req: Request) {
507
+ // 1. Pass req.signal to fetch()
508
+ const upstream = await fetch('https://api.external.com/data', {
509
+ signal: req.signal,
510
+ })
511
+ const payload = await upstream.json()
512
+
513
+ // 2. Pass req.signal to your database queries
514
+ const results = await db.query(sql`SELECT * FROM heavy_analytics`, {
515
+ signal: req.signal,
516
+ })
517
+
518
+ return { payload, results }
519
+ }
520
+ }
521
+ ```
522
+
523
+ ---
524
+
525
+ ## 9. Context & Metadata
319
526
 
320
527
  As your request moves through ExisJS Middlewares, various properties are attached to `req` for your convenience:
321
528