exisjs 0.5.9 → 0.5.11

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 (213) hide show
  1. package/dist/auth/index.d.ts +3 -1
  2. package/dist/auth/index.d.ts.map +1 -1
  3. package/dist/auth/index.js +4 -1
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/auth/oauth/core.d.ts +96 -0
  6. package/dist/auth/oauth/core.d.ts.map +1 -0
  7. package/dist/auth/oauth/core.js +203 -0
  8. package/dist/auth/oauth/core.js.map +1 -0
  9. package/dist/auth/oauth/index.d.ts +45 -0
  10. package/dist/auth/oauth/index.d.ts.map +1 -0
  11. package/dist/auth/oauth/index.js +64 -0
  12. package/dist/auth/oauth/index.js.map +1 -0
  13. package/dist/auth/oauth/middleware.d.ts +63 -0
  14. package/dist/auth/oauth/middleware.d.ts.map +1 -0
  15. package/dist/auth/oauth/middleware.js +164 -0
  16. package/dist/auth/oauth/middleware.js.map +1 -0
  17. package/dist/auth/oauth/providers/custom.d.ts +41 -0
  18. package/dist/auth/oauth/providers/custom.d.ts.map +1 -0
  19. package/dist/auth/oauth/providers/custom.js +204 -0
  20. package/dist/auth/oauth/providers/custom.js.map +1 -0
  21. package/dist/auth/oauth/providers/discord.d.ts +23 -0
  22. package/dist/auth/oauth/providers/discord.d.ts.map +1 -0
  23. package/dist/auth/oauth/providers/discord.js +130 -0
  24. package/dist/auth/oauth/providers/discord.js.map +1 -0
  25. package/dist/auth/oauth/providers/facebook.d.ts +28 -0
  26. package/dist/auth/oauth/providers/facebook.d.ts.map +1 -0
  27. package/dist/auth/oauth/providers/facebook.js +124 -0
  28. package/dist/auth/oauth/providers/facebook.js.map +1 -0
  29. package/dist/auth/oauth/providers/github.d.ts +25 -0
  30. package/dist/auth/oauth/providers/github.d.ts.map +1 -0
  31. package/dist/auth/oauth/providers/github.js +132 -0
  32. package/dist/auth/oauth/providers/github.js.map +1 -0
  33. package/dist/auth/oauth/providers/google.d.ts +23 -0
  34. package/dist/auth/oauth/providers/google.d.ts.map +1 -0
  35. package/dist/auth/oauth/providers/google.js +134 -0
  36. package/dist/auth/oauth/providers/google.js.map +1 -0
  37. package/dist/auth/oauth/providers/microsoft.d.ts +28 -0
  38. package/dist/auth/oauth/providers/microsoft.d.ts.map +1 -0
  39. package/dist/auth/oauth/providers/microsoft.js +136 -0
  40. package/dist/auth/oauth/providers/microsoft.js.map +1 -0
  41. package/dist/auth/oauth/types.d.ts +158 -0
  42. package/dist/auth/oauth/types.d.ts.map +1 -0
  43. package/dist/auth/oauth/types.js +4 -0
  44. package/dist/auth/oauth/types.js.map +1 -0
  45. package/dist/auth/session.d.ts +4 -1
  46. package/dist/auth/session.d.ts.map +1 -1
  47. package/dist/auth/session.js +33 -5
  48. package/dist/auth/session.js.map +1 -1
  49. package/dist/cache/store.d.ts +11 -3
  50. package/dist/cache/store.d.ts.map +1 -1
  51. package/dist/cache/store.js +87 -19
  52. package/dist/cache/store.js.map +1 -1
  53. package/dist/cli/commands/dev.d.ts.map +1 -1
  54. package/dist/cli/commands/dev.js +4 -6
  55. package/dist/cli/commands/dev.js.map +1 -1
  56. package/dist/database/adapters/mongodb.d.ts +58 -0
  57. package/dist/database/adapters/mongodb.d.ts.map +1 -0
  58. package/dist/database/adapters/mongodb.js +264 -0
  59. package/dist/database/adapters/mongodb.js.map +1 -0
  60. package/dist/database/adapters/mysql.d.ts +27 -0
  61. package/dist/database/adapters/mysql.d.ts.map +1 -0
  62. package/dist/database/adapters/mysql.js +188 -0
  63. package/dist/database/adapters/mysql.js.map +1 -0
  64. package/dist/database/adapters/postgres.d.ts +28 -0
  65. package/dist/database/adapters/postgres.d.ts.map +1 -0
  66. package/dist/database/adapters/postgres.js +174 -0
  67. package/dist/database/adapters/postgres.js.map +1 -0
  68. package/dist/database/adapters/sqlite.d.ts +27 -0
  69. package/dist/database/adapters/sqlite.d.ts.map +1 -0
  70. package/dist/database/adapters/sqlite.js +149 -0
  71. package/dist/database/adapters/sqlite.js.map +1 -0
  72. package/dist/database/index.d.ts +42 -0
  73. package/dist/database/index.d.ts.map +1 -0
  74. package/dist/database/index.js +58 -0
  75. package/dist/database/index.js.map +1 -0
  76. package/dist/database/manager.d.ts +114 -0
  77. package/dist/database/manager.d.ts.map +1 -0
  78. package/dist/database/manager.js +269 -0
  79. package/dist/database/manager.js.map +1 -0
  80. package/dist/database/migration.d.ts +57 -0
  81. package/dist/database/migration.d.ts.map +1 -0
  82. package/dist/database/migration.js +329 -0
  83. package/dist/database/migration.js.map +1 -0
  84. package/dist/database/query-builder.d.ts +89 -0
  85. package/dist/database/query-builder.d.ts.map +1 -0
  86. package/dist/database/query-builder.js +353 -0
  87. package/dist/database/query-builder.js.map +1 -0
  88. package/dist/database/types.d.ts +184 -0
  89. package/dist/database/types.d.ts.map +1 -0
  90. package/dist/database/types.js +4 -0
  91. package/dist/database/types.js.map +1 -0
  92. package/dist/docs/ARCHITECTURE.md +349 -0
  93. package/dist/docs/BENCHMARKS.md +36 -0
  94. package/dist/docs/README.md +34 -0
  95. package/dist/docs/content/README.md +44 -0
  96. package/dist/docs/content/adapters.mdx +199 -0
  97. package/dist/docs/content/cache.mdx +239 -0
  98. package/dist/docs/content/circuit-breaker.mdx +130 -0
  99. package/dist/docs/content/cli/commands.mdx +214 -0
  100. package/dist/docs/content/cli/overview.mdx +92 -0
  101. package/dist/docs/content/cli/usage.mdx +61 -0
  102. package/dist/docs/content/config.mdx +214 -0
  103. package/dist/docs/content/controllers.mdx +241 -0
  104. package/dist/docs/content/cron.mdx +109 -0
  105. package/dist/docs/content/database.mdx +209 -0
  106. package/dist/docs/content/dataloader.mdx +170 -0
  107. package/dist/docs/content/decorators.mdx +204 -0
  108. package/dist/docs/content/errors.mdx +344 -0
  109. package/dist/docs/content/gateways.mdx +376 -0
  110. package/dist/docs/content/installation.mdx +210 -0
  111. package/dist/docs/content/introduction.mdx +96 -0
  112. package/dist/docs/content/middleware.mdx +441 -0
  113. package/dist/docs/content/modules.mdx +144 -0
  114. package/dist/docs/content/oauth.mdx +179 -0
  115. package/dist/docs/content/providers.mdx +147 -0
  116. package/dist/docs/content/queues.mdx +177 -0
  117. package/dist/docs/content/requests.mdx +293 -0
  118. package/dist/docs/content/responses.mdx +271 -0
  119. package/dist/docs/content/routing.mdx +541 -0
  120. package/dist/docs/content/rpc.mdx +146 -0
  121. package/dist/docs/content/security.mdx +294 -0
  122. package/dist/docs/content/sse.mdx +159 -0
  123. package/dist/docs/content/structure.mdx +116 -0
  124. package/dist/docs/content/testing.mdx +139 -0
  125. package/dist/docs/content/validation.mdx +249 -0
  126. package/dist/docs/content/websockets.mdx +289 -0
  127. package/dist/docs/integrations/README.md +23 -0
  128. package/dist/docs/integrations/jwt.mdx +151 -0
  129. package/dist/docs/integrations/mongo.mdx +183 -0
  130. package/dist/docs/integrations/observability.mdx +125 -0
  131. package/dist/docs/integrations/openai.mdx +108 -0
  132. package/dist/docs/integrations/postgres.mdx +190 -0
  133. package/dist/docs/integrations/posthog.mdx +122 -0
  134. package/dist/docs/integrations/redis.mdx +141 -0
  135. package/dist/docs/integrations/resend.mdx +117 -0
  136. package/dist/docs/integrations/s3.mdx +122 -0
  137. package/dist/docs/integrations/swagger.mdx +113 -0
  138. package/dist/docs/reference/README.md +3 -0
  139. package/dist/docs/reference/adapters.mdx +130 -0
  140. package/dist/docs/reference/auth.mdx +183 -0
  141. package/dist/docs/reference/cache.mdx +132 -0
  142. package/dist/docs/reference/circuit-breaker.mdx +71 -0
  143. package/dist/docs/reference/config.mdx +118 -0
  144. package/dist/docs/reference/context.mdx +168 -0
  145. package/dist/docs/reference/dataloader.mdx +91 -0
  146. package/dist/docs/reference/decorators.mdx +293 -0
  147. package/dist/docs/reference/di.mdx +123 -0
  148. package/dist/docs/reference/error.mdx +170 -0
  149. package/dist/docs/reference/exis.mdx +124 -0
  150. package/dist/docs/reference/middleware.mdx +304 -0
  151. package/dist/docs/reference/plugin.mdx +113 -0
  152. package/dist/docs/reference/queue.mdx +155 -0
  153. package/dist/docs/reference/router.mdx +248 -0
  154. package/dist/docs/reference/sanitize.mdx +130 -0
  155. package/dist/docs/reference/storage.mdx +116 -0
  156. package/dist/docs/reference/swagger.mdx +132 -0
  157. package/dist/docs/reference/testing.mdx +145 -0
  158. package/dist/docs/reference/validator.mdx +186 -0
  159. package/dist/index.d.ts +4 -0
  160. package/dist/index.d.ts.map +1 -1
  161. package/dist/index.js +10 -1
  162. package/dist/index.js.map +1 -1
  163. package/dist/middleware/backpressure.d.ts.map +1 -1
  164. package/dist/middleware/backpressure.js +21 -12
  165. package/dist/middleware/backpressure.js.map +1 -1
  166. package/dist/middleware/cache.d.ts +6 -0
  167. package/dist/middleware/cache.d.ts.map +1 -1
  168. package/dist/middleware/cache.js +84 -28
  169. package/dist/middleware/cache.js.map +1 -1
  170. package/dist/middleware/idempotency.d.ts +4 -1
  171. package/dist/middleware/idempotency.d.ts.map +1 -1
  172. package/dist/middleware/idempotency.js +29 -4
  173. package/dist/middleware/idempotency.js.map +1 -1
  174. package/dist/middleware/ip-filter.d.ts.map +1 -1
  175. package/dist/middleware/ip-filter.js +16 -0
  176. package/dist/middleware/ip-filter.js.map +1 -1
  177. package/dist/middleware/rate-limit.d.ts.map +1 -1
  178. package/dist/middleware/rate-limit.js +29 -12
  179. package/dist/middleware/rate-limit.js.map +1 -1
  180. package/dist/queue/drivers/MemoryDriver.d.ts +6 -3
  181. package/dist/queue/drivers/MemoryDriver.d.ts.map +1 -1
  182. package/dist/queue/drivers/MemoryDriver.js +76 -23
  183. package/dist/queue/drivers/MemoryDriver.js.map +1 -1
  184. package/dist/server/bootstrapper.d.ts +3 -0
  185. package/dist/server/bootstrapper.d.ts.map +1 -1
  186. package/dist/server/bootstrapper.js +56 -8
  187. package/dist/server/bootstrapper.js.map +1 -1
  188. package/dist/server/bun-adapter.d.ts +2 -0
  189. package/dist/server/bun-adapter.d.ts.map +1 -1
  190. package/dist/server/bun-adapter.js +23 -12
  191. package/dist/server/bun-adapter.js.map +1 -1
  192. package/dist/server/request-handler.d.ts +0 -2
  193. package/dist/server/request-handler.d.ts.map +1 -1
  194. package/dist/server/request-handler.js +2 -24
  195. package/dist/server/request-handler.js.map +1 -1
  196. package/dist/server/request.d.ts.map +1 -1
  197. package/dist/server/request.js +24 -11
  198. package/dist/server/request.js.map +1 -1
  199. package/dist/server/uws-adapter.d.ts +80 -0
  200. package/dist/server/uws-adapter.d.ts.map +1 -0
  201. package/dist/server/uws-adapter.js +357 -0
  202. package/dist/server/uws-adapter.js.map +1 -0
  203. package/dist/server/ws-orchestrator.d.ts +1 -0
  204. package/dist/server/ws-orchestrator.d.ts.map +1 -1
  205. package/dist/server/ws-orchestrator.js +83 -0
  206. package/dist/server/ws-orchestrator.js.map +1 -1
  207. package/dist/types.d.ts +1 -1
  208. package/dist/types.d.ts.map +1 -1
  209. package/dist/utils/config.js.map +1 -1
  210. package/dist/validator/tex.d.ts +21 -16
  211. package/dist/validator/tex.d.ts.map +1 -1
  212. package/dist/validator/tex.js.map +1 -1
  213. package/package.json +11 -3
@@ -0,0 +1,293 @@
1
+ ---
2
+ nextUrl: "/docs/responses"
3
+ nextTitle: "Responses"
4
+ ---
5
+
6
+ # Requests
7
+
8
+ ExisJS wraps the native Node.js `IncomingMessage` object into an `ExisRequest` instance. It abstracts away the complexities of stream parsing, multipart form processing, and header normalization, giving you a typed interface.
9
+
10
+ ---
11
+
12
+ ## 1. Body, Query, and Params
13
+
14
+ The most common request properties are automatically parsed and strongly typed (especially when using the [Schema Validation](/docs/middleware#8-schema-validation) middleware).
15
+
16
+ - **`req.body`**: The parsed body of the request (JSON, URL-encoded, or Multipart).
17
+ - **`req.query`**: An object containing the URL query string parameters (e.g., `?id=123`).
18
+ - **`req.params`**: An object containing route parameters (e.g., `/:id`).
19
+
20
+ #### Functional Paradigm
21
+
22
+ ```typescript filename="src/http/route.ts"
23
+ export default controller({
24
+ updateUser: route.post('/users/:id', {
25
+ handle(req, res) {
26
+ const id = req.params.id // "123"
27
+ const force = req.query.force // "true"
28
+ const payload = req.body // { email: "alice@exisjs.com" }
29
+ }
30
+ })
31
+ })
32
+ ```
33
+
34
+ #### Class-Based (OOP) Paradigm
35
+
36
+ ```typescript filename="src/http/route.ts"
37
+ import { Controller, Post, Param, Query, Body } from 'exisjs/decorators'
38
+
39
+ @Controller()
40
+ export default class UsersController {
41
+ @Post('/users/:id')
42
+ async updateUser(
43
+ @Param('id') id: string,
44
+ @Query('force') force: string,
45
+ @Body() payload: any
46
+ ) {
47
+ // Variables are automatically injected
48
+ }
49
+ }
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 2. On-Demand Parsing
55
+
56
+ To maximize performance, ExisJS does not parse the request body immediately for every route. If you aren't using the Validation middleware, you can manually trigger parsing on-demand.
57
+
58
+ #### Functional Paradigm
59
+
60
+ ```typescript
61
+ export default controller({
62
+ processData: route.post('/process', {
63
+ async handle(req, res) {
64
+ // Parses application/json
65
+ const data = await req.json<{ key: string }>()
66
+
67
+ // Parses raw text (text/plain)
68
+ const text = await req.text()
69
+ }
70
+ })
71
+ })
72
+ ```
73
+
74
+ #### Class-Based (OOP) Paradigm
75
+
76
+ ```typescript filename="src/http/route.ts"
77
+ import { Controller, Post, Req } from 'exisjs/decorators'
78
+
79
+ @Controller()
80
+ export default class ProcessController {
81
+ @Post('/process')
82
+ async processData(@Req() req: any) {
83
+ // Parses application/json
84
+ const data = await req.json<{ key: string }>()
85
+
86
+ // Parses raw text (text/plain)
87
+ const text = await req.text()
88
+ }
89
+ }
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 3. File Uploads & Form Data
95
+
96
+ Handling `multipart/form-data` in Node.js can be difficult. ExisJS provides a stream parser out-of-the-box.
97
+
98
+ Calling `req.formData()` returns both the standard text fields and the uploaded files. ExisJS also attaches a `.saveToDisk()` helper to every uploaded file, which automatically handles writing the buffer and generating collision-free filenames.
99
+
100
+ #### Functional Paradigm
101
+
102
+ ```typescript filename="src/http/upload/route.ts"
103
+ import { controller, route } from 'exisjs/router'
104
+
105
+ export default controller({
106
+ uploadAvatar: route.post('/avatar', {
107
+ async handle(req, res) {
108
+ // 1. Parse the multipart form data
109
+ const { fields, files } = await req.formData()
110
+
111
+ // 2. Access the file (e.g., from an input named "profile_pic")
112
+ const uploadedFile = files['profile_pic']
113
+
114
+ if (uploadedFile) {
115
+ // 3. Save it to disk safely. Returns the absolute saved path.
116
+ const savedPath = await uploadedFile.saveToDisk('./uploads/avatars')
117
+ return { success: true, path: savedPath }
118
+ }
119
+
120
+ return { success: false, error: "No file uploaded" }
121
+ }
122
+ })
123
+ })
124
+ ```
125
+
126
+ #### Class-Based (OOP) Paradigm
127
+
128
+ ```typescript filename="src/http/upload/route.ts"
129
+ import { Controller, Post, UploadedFile } from 'exisjs/decorators'
130
+
131
+ @Controller()
132
+ export default class UploadController {
133
+ @Post('/avatar')
134
+ async uploadAvatar(@UploadedFile('profile_pic') profilePic: any) {
135
+ if (profilePic) {
136
+ // Save it to disk safely. Returns the absolute saved path.
137
+ const savedPath = await profilePic.saveToDisk('./uploads/avatars')
138
+ return { success: true, path: savedPath }
139
+ }
140
+
141
+ return { success: false, error: "No file uploaded" }
142
+ }
143
+ }
144
+ ```
145
+
146
+ #### Streaming (`streamUpload`)
147
+
148
+ If you are dealing with large files and don't want to buffer *anything* in memory, ExisJS provides a direct streaming helper. It pipes the multipart chunks directly to the disk in real-time.
149
+
150
+ #### Functional Paradigm
151
+
152
+ ```typescript
153
+ import { controller, route } from 'exisjs/router'
154
+
155
+ export default controller({
156
+ uploadCover: route.post('/cover', {
157
+ async handle(req, res) {
158
+ // 1. Using the method on the request object
159
+ // const { fields, files } = await req.streamUpload('./uploads/covers')
160
+
161
+ // 2. Or using the exported helper from exisjs/storage
162
+ const { streamUpload } = await import('exisjs/storage')
163
+ const { fields, files } = await streamUpload(req, { dest: './uploads/covers' })
164
+
165
+ return {
166
+ success: true,
167
+ uploadedFiles: files.map(f => f.filename),
168
+ customFields: fields
169
+ }
170
+ }
171
+ })
172
+ })
173
+ ```
174
+
175
+ #### Class-Based (OOP) Paradigm
176
+
177
+ ```typescript
178
+ import { Controller, Post, Req } from 'exisjs/decorators'
179
+ import { streamUpload } from 'exisjs/storage'
180
+
181
+ @Controller()
182
+ export default class UploadController {
183
+ @Post('/cover')
184
+ async uploadCover(@Req() req: any) {
185
+ // Both req.streamUpload() and the exported helper from exisjs/storage work.
186
+ const { fields, files } = await streamUpload(req, { dest: './uploads/covers' })
187
+
188
+ return {
189
+ success: true,
190
+ uploadedFiles: files.map(f => f.filename),
191
+ customFields: fields
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 4. HTTP Headers & Content Negotiation
200
+
201
+ ```typescript
202
+ // Get a specific header (case-insensitive)
203
+ const authHeader = req.get('Authorization')
204
+ // or
205
+ const authHeader = req.header('authorization')
206
+
207
+ // Check the Content-Type
208
+ if (req.is('application/json')) { ... }
209
+
210
+ // Content Negotiation (returns the best match)
211
+ const format = req.accepts('html', 'json', 'text')
212
+ const language = req.acceptsLanguages('en', 'es', 'fr')
213
+ ```
214
+
215
+ ---
216
+
217
+ ## 5. Network Information
218
+
219
+ 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`.
220
+
221
+ - **`req.ip`**: The client's IP address.
222
+ - **`req.ips`**: An array of IPs if multiple proxies were traversed.
223
+ - **`req.hostname`**: The resolved hostname (from `Host` or `X-Forwarded-Host`).
224
+ - **`req.protocol`**: Returns `'http'` or `'https'`.
225
+ - **`req.secure`**: A boolean (true if `req.protocol === 'https'`).
226
+
227
+ ---
228
+
229
+ ## 6. Context & Metadata
230
+
231
+ As your request moves through ExisJS Middlewares, various properties are attached to `req` for your convenience:
232
+
233
+ - **`req.log`**: A request-scoped instance of the Pino logger (via `requestLogger`).
234
+ - **`req.requestId`**: The unique Trace ID (via `requestId`).
235
+ - **`req.user`**: The authenticated user payload (via `JWT` auth middleware).
236
+ - **`req.session`**: The stateful session object (via `session` middleware).
237
+ - **`req.fresh`**: Returns `true` if the client's cache is still valid (ETag match), meaning you can send a 304 response.
238
+
239
+ ### Inline Context Typing (Per-Route)
240
+
241
+ If you don't want to create a globally typed router with `createRouter`, ExisJS natively types `req.user` as `any` by default so you never get annoying compiler errors (e.g. `req.user.userId` just works!).
242
+
243
+ However, if you want **strict** type safety on a specific route without global configuration, you can pass your custom context directly to the route's 4th generic parameter:
244
+
245
+ ```typescript
246
+ import { route } from "exisjs/router";
247
+
248
+ export default route.get<any, any, any, { user: { userId: string } }>("/", {
249
+ async handle({ req }) {
250
+ // Typed. No "as any" needed.
251
+ const id = req.user.userId;
252
+ }
253
+ })
254
+ ```
255
+
256
+ ### Strongly-Typed Context & Native Destructuring (App-Wide)
257
+
258
+ To provide a better developer experience, ExisJS allows you to define a **custom typed context** for your application. This completely eliminates the need for global `.d.ts` augmentations and `as any` casts.
259
+
260
+ To do this, create a custom router factory (e.g., `src/http/router.ts`) and define the exact shape of your application's context (like the user object, workspace, tenant ID, etc).
261
+
262
+ ```typescript filename="src/http/router.ts"
263
+ import { createRouter } from "exisjs/router";
264
+
265
+ // 1. Define everything your middlewares will attach to the request
266
+ interface AppContext {
267
+ user: { id: string; role: string };
268
+ workspace: string;
269
+ }
270
+
271
+ // 2. Export a custom typed route and controller
272
+ export const { route, controller } = createRouter<AppContext>();
273
+ ```
274
+
275
+ Now, instead of importing from `exisjs/router`, import from your **local** router. Your handlers can natively destructure your custom properties.
276
+
277
+ ```typescript filename="src/http/users/route.ts"
278
+ import { route, controller } from "../../router.ts";
279
+
280
+ export default controller({
281
+ getProfile: route.get("/", {
282
+ async handle({ user, workspace }) {
283
+ // Strongly typed.
284
+ // No more `req.user`, no more `as any`.
285
+ console.log(user.role, workspace);
286
+ }
287
+ })
288
+ });
289
+ ```
290
+
291
+ <Warning>
292
+ **Important Middleware Rule:** In order for destructuring to work (e.g. `handle({ user })`), the `user` property MUST be populated **before** the handler executes. This means you must populate it inside a `middleware` instead of calling an inline function inside the handler.
293
+ </Warning>
@@ -0,0 +1,271 @@
1
+ ---
2
+ nextUrl: "/docs/prisma"
3
+ nextTitle: "Prisma Integration"
4
+ ---
5
+
6
+ # Responses
7
+
8
+ Unlike standard Node.js `ServerResponse` objects, ExisJS wraps every response in an `ExisResponse` class. It provides a chainable API while maintaining the raw performance of native Node.
9
+
10
+ While ExisJS routes allow you to simply `return { data: ... }` (which automatically serializes to JSON), the `res` object gives you granular control over headers, cookies, streams, and file downloads.
11
+
12
+ ---
13
+
14
+ ## 1. Returning Data vs Sending Data
15
+
16
+ The most common way to respond to a request in ExisJS is to simply **return an object or array**. ExisJS intercepts the return value, automatically serializes it to JSON, sets the `Content-Type`, calculates the `Content-Length`, and sends it down the wire.
17
+
18
+ #### Functional Paradigm
19
+
20
+ ```typescript filename="src/http/route.ts"
21
+ import { controller, route } from 'exisjs/router'
22
+
23
+ export default controller({
24
+ getUsers: route.get('/', {
25
+ handle(req, res) {
26
+ // ExisJS automatically handles JSON serialization
27
+ return [{ id: 1, name: "Alice" }]
28
+ }
29
+ })
30
+ })
31
+ ```
32
+
33
+ #### Class-Based (OOP) Paradigm
34
+
35
+ ```typescript filename="src/http/route.ts"
36
+ import { Controller, Get } from 'exisjs/decorators'
37
+
38
+ @Controller()
39
+ export default class UsersController {
40
+ @Get('/')
41
+ async getUsers() {
42
+ // ExisJS automatically handles JSON serialization
43
+ return [{ id: 1, name: "Alice" }]
44
+ }
45
+ }
46
+ ```
47
+
48
+ However, you can completely bypass this and use the `res` object manually:
49
+
50
+ #### Functional Paradigm
51
+
52
+ ```typescript filename="src/http/route.ts"
53
+ export default controller({
54
+ getUsers: route.get('/', {
55
+ handle(req, res) {
56
+ // Chainable response API
57
+ res.status(201).json([{ id: 1, name: "Alice" }])
58
+ }
59
+ })
60
+ })
61
+ ```
62
+
63
+ #### Class-Based (OOP) Paradigm
64
+
65
+ ```typescript filename="src/http/route.ts"
66
+ import { Controller, Get, Res } from 'exisjs/decorators'
67
+
68
+ @Controller()
69
+ export default class UsersController {
70
+ @Get('/')
71
+ async getUsers(@Res() res: any) {
72
+ // Chainable response API
73
+ res.status(201).json([{ id: 1, name: "Alice" }])
74
+ }
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## 2. Standardized JSON Responses (`exisjs/response`)
81
+
82
+ To keep your APIs consistent, ExisJS exports standard `success()` and `error()` builders.
83
+ Using these ensures your entire application adheres to a unified `{ success: boolean, data?: any, error?: any }` structure, which frontend teams love.
84
+
85
+ #### Functional Paradigm
86
+
87
+ ```typescript filename="src/http/route.ts"
88
+ import { controller, route } from 'exisjs/router'
89
+ import { success, error } from 'exisjs/response'
90
+
91
+ export default controller({
92
+ getUser: route.get('/:id', {
93
+ handle(req, res) {
94
+ const user = db.users.find(req.params.id)
95
+
96
+ if (!user) {
97
+ res.status(404)
98
+ // Generates: { success: false, error: { message: "User not found", code: "NOT_FOUND" } }
99
+ return error("User not found", "NOT_FOUND")
100
+ }
101
+
102
+ // Generates: { success: true, data: { id: 1, name: "Alice" }, message: "Found user" }
103
+ return success(user, "Found user")
104
+ }
105
+ })
106
+ })
107
+ ```
108
+
109
+ #### Class-Based (OOP) Paradigm
110
+
111
+ ```typescript filename="src/http/route.ts"
112
+ import { Controller, Get, Param, Res } from 'exisjs/decorators'
113
+ import { success, error } from 'exisjs/response'
114
+
115
+ @Controller()
116
+ export default class UsersController {
117
+ @Get('/:id')
118
+ async getUser(@Param('id') id: string, @Res() res: any) {
119
+ const user = db.users.find(id)
120
+
121
+ if (!user) {
122
+ res.status(404)
123
+ // Generates: { success: false, error: { message: "User not found", code: "NOT_FOUND" } }
124
+ return error("User not found", "NOT_FOUND")
125
+ }
126
+
127
+ // Generates: { success: true, data: { id: 1, name: "Alice" }, message: "Found user" }
128
+ return success(user, "Found user")
129
+ }
130
+ }
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 3. Setting Headers & Status
136
+
137
+ The `ExisResponse` object implements a chainable API for HTTP headers and status codes.
138
+
139
+ ```typescript
140
+ res.status(404)
141
+ .set('X-Custom-Header', 'exis-is-awesome')
142
+ .append('Set-Cookie', 'foo=bar')
143
+ .json({ error: 'Not Found' })
144
+ ```
145
+
146
+ ### Supported Header Methods
147
+ - `res.status(code)`: Sets the HTTP status code.
148
+ - `res.set(key, value)` (or `res.header()`): Sets an HTTP header.
149
+ - `res.append(key, value)`: Appends to an existing header (e.g., multiple `Set-Cookie` directives).
150
+ - `res.removeHeader(key)`: Deletes a header.
151
+ - `res.type(mimeType)`: A shorthand for setting the `Content-Type` header (e.g., `res.type('html')` becomes `text/html`).
152
+ - `res.vary(field)`: Adds a field to the `Vary` header for caching.
153
+
154
+ ---
155
+
156
+ ## 3. Cookies
157
+
158
+ ExisJS provides native helper methods for setting and clearing cookies safely.
159
+
160
+ ```typescript
161
+ // Set a secure, HTTP-only cookie
162
+ res.cookie('session_id', '123456', {
163
+ maxAge: 86400, // 1 day in seconds
164
+ httpOnly: true,
165
+ secure: true,
166
+ sameSite: 'Strict'
167
+ })
168
+
169
+ // Clear a cookie
170
+ res.clearCookie('session_id')
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 4. File Downloads & Streaming
176
+
177
+ ExisJS makes streaming files simple, bypassing V8 memory bottlenecks by piping native Node streams directly to the socket.
178
+
179
+ ### `res.download()`
180
+ Automatically reads a file from disk, sets the `Content-Disposition` header to force a download in the browser, and infers the correct MIME type based on the file extension.
181
+
182
+ #### Functional Paradigm
183
+
184
+ ```typescript
185
+ import { controller, route } from 'exisjs/router'
186
+
187
+ export default controller({
188
+ downloadReport: route.get('/report', {
189
+ handle(req, res) {
190
+ res.download('/absolute/path/to/report.pdf', 'Q4-Report.pdf')
191
+ }
192
+ })
193
+ })
194
+ ```
195
+
196
+ #### Class-Based (OOP) Paradigm
197
+
198
+ ```typescript
199
+ import { Controller, Get, Res } from 'exisjs/decorators'
200
+
201
+ @Controller()
202
+ export default class ReportController {
203
+ @Get('/report')
204
+ async downloadReport(@Res() res: any) {
205
+ res.download('/absolute/path/to/report.pdf', 'Q4-Report.pdf')
206
+ }
207
+ }
208
+ ```
209
+
210
+ ### `res.sendStream()`
211
+ If you are fetching data from an external source (like AWS S3) or generating data on the fly, you can pipe any `ReadableStream` directly to the client.
212
+
213
+ #### Functional Paradigm
214
+
215
+ ```typescript
216
+ import { controller, route } from 'exisjs/router'
217
+ import fs from 'node:fs'
218
+
219
+ export default controller({
220
+ streamVideo: route.get('/video', {
221
+ handle(req, res) {
222
+ const videoStream = fs.createReadStream('/path/to/video.mp4')
223
+
224
+ res.type('video/mp4')
225
+ res.sendStream(videoStream)
226
+ }
227
+ })
228
+ })
229
+ ```
230
+
231
+ #### Class-Based (OOP) Paradigm
232
+
233
+ ```typescript
234
+ import { Controller, Get, Res } from 'exisjs/decorators'
235
+ import fs from 'node:fs'
236
+
237
+ @Controller()
238
+ export default class VideoController {
239
+ @Get('/video')
240
+ async streamVideo(@Res() res: any) {
241
+ const videoStream = fs.createReadStream('/path/to/video.mp4')
242
+
243
+ res.type('video/mp4')
244
+ res.sendStream(videoStream)
245
+ }
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## 5. Redirects
252
+
253
+ You can instantly redirect users to another URL using `res.redirect()`. It defaults to a `302 Found` temporary redirect, but you can override the status code.
254
+
255
+ ```typescript
256
+ // Temporary Redirect (302)
257
+ res.redirect('/login')
258
+
259
+ // Permanent Redirect (301)
260
+ res.redirect('https://google.com', 301)
261
+ ```
262
+
263
+ ---
264
+
265
+ ## 6. ETags & Conditional Requests (304 Not Modified)
266
+
267
+ ExisJS features native, automatic ETag generation.
268
+
269
+ If the client sends an `If-None-Match` header matching the ETag of the current response, ExisJS will immediately abort serialization and send a `304 Not Modified` header with an empty body, saving bandwidth and CPU cycles.
270
+
271
+ *Note: ETag generation is disabled by default, but can be enabled globally in your `exis()` config, or manually per-route via `res.etagEnabled = true`.*