create-acrylblends 0.0.0-stage → 0.1.0

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 (106) hide show
  1. package/bin/create-acrylblends.js +4 -0
  2. package/dist/index.d.ts +56 -0
  3. package/dist/index.js +274 -0
  4. package/dist/index.js.map +1 -0
  5. package/docs/framework/LICENSE +23 -0
  6. package/docs/framework/README.md +63 -0
  7. package/docs/framework/UPSTREAM.md +3 -0
  8. package/docs/framework/agents/index.md +24 -0
  9. package/docs/framework/api/acryl.md +259 -0
  10. package/docs/framework/api/context.md +527 -0
  11. package/docs/framework/api/exception.md +80 -0
  12. package/docs/framework/api/index.md +12 -0
  13. package/docs/framework/api/presets.md +65 -0
  14. package/docs/framework/api/request.md +396 -0
  15. package/docs/framework/api/routing.md +303 -0
  16. package/docs/framework/api/summary.md +140 -0
  17. package/docs/framework/concepts/benchmarks.md +26 -0
  18. package/docs/framework/concepts/developer-experience.md +5 -0
  19. package/docs/framework/concepts/middleware.md +21 -0
  20. package/docs/framework/concepts/motivation.md +14 -0
  21. package/docs/framework/concepts/routers.md +63 -0
  22. package/docs/framework/concepts/stacks.md +235 -0
  23. package/docs/framework/concepts/web-standard.md +37 -0
  24. package/docs/framework/cordis/acryl-in-a-cordis-app.md +146 -0
  25. package/docs/framework/cordis/concepts.md +105 -0
  26. package/docs/framework/cordis/middleware.md +8 -0
  27. package/docs/framework/cordis/migration-from-0-3.md +111 -0
  28. package/docs/framework/cordis/migration-from-0-4.md +14 -0
  29. package/docs/framework/cordis/migration-from-dsh-webserver.md +103 -0
  30. package/docs/framework/cordis/mounting.md +85 -0
  31. package/docs/framework/cordis/node-server.md +75 -0
  32. package/docs/framework/cordis/plugin-lifecycle.md +111 -0
  33. package/docs/framework/cordis/surfaces.md +71 -0
  34. package/docs/framework/cordis/transport-and-cancellation.md +57 -0
  35. package/docs/framework/cordis/validation.md +135 -0
  36. package/docs/framework/getting-started/basic.md +196 -0
  37. package/docs/framework/getting-started/bun.md +221 -0
  38. package/docs/framework/getting-started/nodejs.md +344 -0
  39. package/docs/framework/getting-started/other-runtimes.md +23 -0
  40. package/docs/framework/guides/best-practices.md +231 -0
  41. package/docs/framework/guides/create-acrylblends.md +79 -0
  42. package/docs/framework/guides/examples.md +3 -0
  43. package/docs/framework/guides/faq.md +19 -0
  44. package/docs/framework/guides/helpers.md +33 -0
  45. package/docs/framework/guides/jsx-dom.md +299 -0
  46. package/docs/framework/guides/jsx.md +440 -0
  47. package/docs/framework/guides/middleware.md +253 -0
  48. package/docs/framework/guides/rpc.md +886 -0
  49. package/docs/framework/guides/testing.md +133 -0
  50. package/docs/framework/guides/validation.md +383 -0
  51. package/docs/framework/helpers/accepts.md +60 -0
  52. package/docs/framework/helpers/adapter.md +87 -0
  53. package/docs/framework/helpers/conninfo.md +112 -0
  54. package/docs/framework/helpers/cookie.md +232 -0
  55. package/docs/framework/helpers/css.md +285 -0
  56. package/docs/framework/helpers/dev.md +67 -0
  57. package/docs/framework/helpers/factory.md +180 -0
  58. package/docs/framework/helpers/html.md +123 -0
  59. package/docs/framework/helpers/jwt.md +184 -0
  60. package/docs/framework/helpers/proxy.md +118 -0
  61. package/docs/framework/helpers/route.md +143 -0
  62. package/docs/framework/helpers/ssg.md +393 -0
  63. package/docs/framework/helpers/streaming.md +119 -0
  64. package/docs/framework/helpers/testing.md +101 -0
  65. package/docs/framework/helpers/websocket.md +215 -0
  66. package/docs/framework/middleware/builtin/basic-auth.md +165 -0
  67. package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
  68. package/docs/framework/middleware/builtin/body-limit.md +71 -0
  69. package/docs/framework/middleware/builtin/cache.md +122 -0
  70. package/docs/framework/middleware/builtin/combine.md +94 -0
  71. package/docs/framework/middleware/builtin/compress.md +59 -0
  72. package/docs/framework/middleware/builtin/context-storage.md +78 -0
  73. package/docs/framework/middleware/builtin/cors.md +135 -0
  74. package/docs/framework/middleware/builtin/csrf.md +105 -0
  75. package/docs/framework/middleware/builtin/etag.md +54 -0
  76. package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
  77. package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
  78. package/docs/framework/middleware/builtin/jwk.md +192 -0
  79. package/docs/framework/middleware/builtin/jwt.md +145 -0
  80. package/docs/framework/middleware/builtin/language.md +232 -0
  81. package/docs/framework/middleware/builtin/logger.md +74 -0
  82. package/docs/framework/middleware/builtin/loopback-only.md +31 -0
  83. package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
  84. package/docs/framework/middleware/builtin/method-override.md +80 -0
  85. package/docs/framework/middleware/builtin/mount.md +106 -0
  86. package/docs/framework/middleware/builtin/pretty-json.md +53 -0
  87. package/docs/framework/middleware/builtin/request-id.md +94 -0
  88. package/docs/framework/middleware/builtin/secure-headers.md +247 -0
  89. package/docs/framework/middleware/builtin/timeout.md +98 -0
  90. package/docs/framework/middleware/builtin/timing.md +101 -0
  91. package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
  92. package/docs/framework/middleware/third-party.md +4 -0
  93. package/docs/framework/upstream.json +9 -0
  94. package/package.json +47 -3
  95. package/templates/bun/gitignore +1 -0
  96. package/templates/bun/package.json +19 -0
  97. package/templates/bun/src/index.ts +7 -0
  98. package/templates/bun/test/app.test.ts +8 -0
  99. package/templates/bun/tsconfig.json +15 -0
  100. package/templates/nodejs/gitignore +1 -0
  101. package/templates/nodejs/package.json +22 -0
  102. package/templates/nodejs/src/app.ts +7 -0
  103. package/templates/nodejs/src/index.ts +9 -0
  104. package/templates/nodejs/test/app.test.ts +9 -0
  105. package/templates/nodejs/tsconfig.json +16 -0
  106. package/README.md +0 -3
@@ -0,0 +1,440 @@
1
+ # JSX
2
+
3
+ > **Not yet available.** The JSX packages (`acrylblends/jsx` and its subpaths) is documented here so the page structure matches the rest of the framework, and is not part of acrylblends yet. The code on this page is not checked by the docs tests.
4
+
5
+
6
+ You can write HTML with JSX syntax with `acrylblends/jsx`.
7
+
8
+ Although `acrylblends/jsx` works on the client, you will probably use it most often when rendering content on the server side. Here are some things related to JSX that are common to both server and client.
9
+
10
+ ## Settings
11
+
12
+ To use JSX, modify the `tsconfig.json`:
13
+
14
+ `tsconfig.json`:
15
+
16
+ ```json
17
+ {
18
+ "compilerOptions": {
19
+ "jsx": "react-jsx",
20
+ "jsxImportSource": "acrylblends/jsx"
21
+ }
22
+ }
23
+ ```
24
+
25
+ Alternatively, use the pragma directives:
26
+
27
+ ```ts
28
+ /** @jsx jsx */
29
+ /** @jsxImportSource acrylblends/jsx */
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ > **Note.** If you are coming straight from the [Quick Start](../README.md#quick-start), the main file has a `.ts` extension - you need to change it to `.tsx` - otherwise you will not be able to run the application at all. You should additionally modify the `package.json` (or `deno.json` if you are using Deno) to reflect that change (e.g. instead of having `bun run --hot src/index.ts` in dev script, you should have `bun run --hot src/index.tsx`).
35
+
36
+ `index.tsx`:
37
+
38
+ ```tsx
39
+ import { Acryl } from 'acrylblends'
40
+ import type { FC } from 'acrylblends/jsx'
41
+
42
+ const app = new Acryl()
43
+
44
+ const Layout: FC = (props) => {
45
+ return (
46
+ <html>
47
+ <body>{props.children}</body>
48
+ </html>
49
+ )
50
+ }
51
+
52
+ const Top: FC<{ messages: string[] }> = (props: {
53
+ messages: string[]
54
+ }) => {
55
+ return (
56
+ <Layout>
57
+ <h1>Hello Acryl!</h1>
58
+ <ul>
59
+ {props.messages.map((message) => {
60
+ return <li>{message}!!</li>
61
+ })}
62
+ </ul>
63
+ </Layout>
64
+ )
65
+ }
66
+
67
+ app.get('/', (c) => {
68
+ const messages = ['Good Morning', 'Good Evening', 'Good Night']
69
+ return c.html(<Top messages={messages} />)
70
+ })
71
+
72
+ export default app
73
+ ```
74
+
75
+ ## Metadata hoisting
76
+
77
+ You can write document metadata tags such as `<title>`, `<link>`, and `<meta>` directly inside your components. These tags will be automatically hoisted to the `<head>` section of the document. This is especially useful when the `<head>` element is rendered far from the component that determines the appropriate metadata.
78
+
79
+ ```tsx
80
+ import { Acryl } from 'acrylblends'
81
+
82
+ const app = new Acryl()
83
+
84
+ app.use('*', async (c, next) => {
85
+ c.setRenderer((content) => {
86
+ return c.html(
87
+ <html>
88
+ <head></head>
89
+ <body>{content}</body>
90
+ </html>
91
+ )
92
+ })
93
+ await next()
94
+ })
95
+
96
+ app.get('/about', (c) => {
97
+ return c.render(
98
+ <>
99
+ <title>About Page</title>
100
+ <meta name='description' content='This is the about page.' />
101
+ about page content
102
+ </>
103
+ )
104
+ })
105
+
106
+ export default app
107
+ ```
108
+
109
+ > **Note.** When hoisting occurs, existing elements are not removed. Elements appearing later are added to the end. For example, if you have `<title>Default</title>` in your `<head>` and a component renders `<title>Page Title</title>`, both titles will appear in the head.
110
+
111
+ ## Fragment
112
+
113
+ Use Fragment to group multiple elements without adding extra nodes:
114
+
115
+ ```tsx
116
+ import { Fragment } from 'acrylblends/jsx'
117
+
118
+ const List = () => (
119
+ <Fragment>
120
+ <p>first child</p>
121
+ <p>second child</p>
122
+ <p>third child</p>
123
+ </Fragment>
124
+ )
125
+ ```
126
+
127
+ Or you can write it with `<></>` if it sets up properly.
128
+
129
+ ```tsx
130
+ const List = () => (
131
+ <>
132
+ <p>first child</p>
133
+ <p>second child</p>
134
+ <p>third child</p>
135
+ </>
136
+ )
137
+ ```
138
+
139
+ ## `PropsWithChildren`
140
+
141
+ You can use `PropsWithChildren` to correctly infer a child element in a function component.
142
+
143
+ ```tsx
144
+ import { PropsWithChildren } from 'acrylblends/jsx'
145
+
146
+ type Post = {
147
+ id: number
148
+ title: string
149
+ }
150
+
151
+ function Component({ title, children }: PropsWithChildren<Post>) {
152
+ return (
153
+ <div>
154
+ <h1>{title}</h1>
155
+ {children}
156
+ </div>
157
+ )
158
+ }
159
+ ```
160
+
161
+ ## Inserting Raw HTML
162
+
163
+ To directly insert HTML, use `dangerouslySetInnerHTML`:
164
+
165
+ ```tsx
166
+ app.get('/foo', (c) => {
167
+ const inner = { __html: 'JSX &middot; SSR' }
168
+ const Div = <div dangerouslySetInnerHTML={inner} />
169
+ })
170
+ ```
171
+
172
+ ## Memoization
173
+
174
+ Optimize your components by memoizing computed strings using `memo`:
175
+
176
+ ```tsx
177
+ import { memo } from 'acrylblends/jsx'
178
+
179
+ const Header = memo(() => <header>Welcome to Acryl</header>)
180
+ const Footer = memo(() => <footer>Powered by Acryl</footer>)
181
+ const Layout = (
182
+ <div>
183
+ <Header />
184
+ <p>Acryl is cool!</p>
185
+ <Footer />
186
+ </div>
187
+ )
188
+ ```
189
+
190
+ ## Context
191
+
192
+ By using `useContext`, you can share data globally across any level of the Component tree without passing values through props.
193
+
194
+ ```tsx
195
+ import type { FC } from 'acrylblends/jsx'
196
+ import { createContext, useContext } from 'acrylblends/jsx'
197
+
198
+ const themes = {
199
+ light: {
200
+ color: '#000000',
201
+ background: '#eeeeee',
202
+ },
203
+ dark: {
204
+ color: '#ffffff',
205
+ background: '#222222',
206
+ },
207
+ }
208
+
209
+ const ThemeContext = createContext(themes.light)
210
+
211
+ const Button: FC = () => {
212
+ const theme = useContext(ThemeContext)
213
+ return <button style={theme}>Push!</button>
214
+ }
215
+
216
+ const Toolbar: FC = () => {
217
+ return (
218
+ <div>
219
+ <Button />
220
+ </div>
221
+ )
222
+ }
223
+
224
+ // ...
225
+
226
+ app.get('/', (c) => {
227
+ return c.html(
228
+ <div>
229
+ <ThemeContext.Provider value={themes.dark}>
230
+ <Toolbar />
231
+ </ThemeContext.Provider>
232
+ </div>
233
+ )
234
+ })
235
+ ```
236
+
237
+ ## Async Component
238
+
239
+ `acrylblends/jsx` supports an Async Component, so you can use `async`/`await` in your component.
240
+ If you render it with `c.html()`, it will await automatically.
241
+
242
+ ```tsx
243
+ const AsyncComponent = async () => {
244
+ await new Promise((r) => setTimeout(r, 1000)) // sleep 1s
245
+ return <div>Done!</div>
246
+ }
247
+
248
+ app.get('/', (c) => {
249
+ return c.html(
250
+ <html>
251
+ <body>
252
+ <AsyncComponent />
253
+ </body>
254
+ </html>
255
+ )
256
+ })
257
+ ```
258
+
259
+ ## Suspense *(Experimental)*
260
+
261
+ The React-like `Suspense` feature is available.
262
+ If you wrap the async component with `Suspense`, the content in the fallback will be rendered first, and once the Promise is resolved, the awaited content will be displayed.
263
+ You can use it with `renderToReadableStream()`.
264
+
265
+ ```tsx
266
+ import { renderToReadableStream, Suspense } from 'acrylblends/jsx/streaming'
267
+
268
+ //...
269
+
270
+ app.get('/', (c) => {
271
+ const stream = renderToReadableStream(
272
+ <html>
273
+ <body>
274
+ <Suspense fallback={<div>loading...</div>}>
275
+ <Component />
276
+ </Suspense>
277
+ </body>
278
+ </html>
279
+ )
280
+ return c.body(stream, {
281
+ headers: {
282
+ 'Content-Type': 'text/html; charset=UTF-8',
283
+ 'Transfer-Encoding': 'chunked',
284
+ },
285
+ })
286
+ })
287
+ ```
288
+
289
+ ## ErrorBoundary *(Experimental)*
290
+
291
+ You can catch errors in child components using `ErrorBoundary`.
292
+
293
+ In the example below, it will show the content specified in `fallback` if an error occurs.
294
+
295
+ ```tsx
296
+ function SyncComponent() {
297
+ throw new Error('Error')
298
+ return <div>Hello</div>
299
+ }
300
+
301
+ app.get('/sync', async (c) => {
302
+ return c.html(
303
+ <html>
304
+ <body>
305
+ <ErrorBoundary fallback={<div>Out of Service</div>}>
306
+ <SyncComponent />
307
+ </ErrorBoundary>
308
+ </body>
309
+ </html>
310
+ )
311
+ })
312
+ ```
313
+
314
+ `ErrorBoundary` can also be used with async components and `Suspense`.
315
+
316
+ ```tsx
317
+ async function AsyncComponent() {
318
+ await new Promise((resolve) => setTimeout(resolve, 2000))
319
+ throw new Error('Error')
320
+ return <div>Hello</div>
321
+ }
322
+
323
+ app.get('/with-suspense', async (c) => {
324
+ return c.html(
325
+ <html>
326
+ <body>
327
+ <ErrorBoundary fallback={<div>Out of Service</div>}>
328
+ <Suspense fallback={<div>Loading...</div>}>
329
+ <AsyncComponent />
330
+ </Suspense>
331
+ </ErrorBoundary>
332
+ </body>
333
+ </html>
334
+ )
335
+ })
336
+ ```
337
+
338
+ ## StreamingContext *(Experimental)*
339
+
340
+ You can use `StreamingContext` to provide configuration for streaming components like `Suspense` and `ErrorBoundary`. This is useful for adding nonce values to script tags generated by these components for Content Security Policy (CSP).
341
+
342
+ ```tsx
343
+ import { Suspense, StreamingContext } from 'acrylblends/jsx/streaming'
344
+
345
+ // ...
346
+
347
+ app.get('/', (c) => {
348
+ const stream = renderToReadableStream(
349
+ <html>
350
+ <body>
351
+ <StreamingContext
352
+ value={{ scriptNonce: 'random-nonce-value' }}
353
+ >
354
+ <Suspense fallback={<div>Loading...</div>}>
355
+ <AsyncComponent />
356
+ </Suspense>
357
+ </StreamingContext>
358
+ </body>
359
+ </html>
360
+ )
361
+
362
+ return c.body(stream, {
363
+ headers: {
364
+ 'Content-Type': 'text/html; charset=UTF-8',
365
+ 'Transfer-Encoding': 'chunked',
366
+ 'Content-Security-Policy':
367
+ "script-src 'nonce-random-nonce-value'",
368
+ },
369
+ })
370
+ })
371
+ ```
372
+
373
+ The `scriptNonce` value will be automatically added to any `<script>` tags generated by `Suspense` and `ErrorBoundary` components.
374
+
375
+ ## Integration with html Middleware
376
+
377
+ Combine the JSX and HTML middlewares for powerful templating.
378
+ For in-depth details, consult the [HTML middleware documentation](../helpers/html.md).
379
+
380
+ ```tsx
381
+ import { Acryl } from 'acrylblends'
382
+ import { html } from 'acrylblends/html'
383
+
384
+ const app = new Acryl()
385
+
386
+ interface SiteData {
387
+ title: string
388
+ children?: any
389
+ }
390
+
391
+ const Layout = (props: SiteData) =>
392
+ html`<!doctype html>
393
+ <html>
394
+ <head>
395
+ <title>${props.title}</title>
396
+ </head>
397
+ <body>
398
+ ${props.children}
399
+ </body>
400
+ </html>`
401
+
402
+ const Content = (props: { siteData: SiteData; name: string }) => (
403
+ <Layout {...props.siteData}>
404
+ <h1>Hello {props.name}</h1>
405
+ </Layout>
406
+ )
407
+
408
+ app.get('/:name', (c) => {
409
+ const { name } = c.req.param()
410
+ const props = {
411
+ name: name,
412
+ siteData: {
413
+ title: 'JSX with html sample',
414
+ },
415
+ }
416
+ return c.html(<Content {...props} />)
417
+ })
418
+
419
+ export default app
420
+ ```
421
+
422
+ ## With JSX Renderer Middleware
423
+
424
+ The [JSX Renderer Middleware](../middleware/builtin/jsx-renderer.md) allows you to create HTML pages more easily with the JSX.
425
+
426
+ ## Override type definitions
427
+
428
+ You can override the type definition to add your custom elements and attributes.
429
+
430
+ ```ts
431
+ declare module 'acrylblends/jsx' {
432
+ namespace JSX {
433
+ interface IntrinsicElements {
434
+ 'my-custom-element': HTMLAttributes & {
435
+ 'x-event'?: 'click' | 'scroll'
436
+ }
437
+ }
438
+ }
439
+ }
440
+ ```
@@ -0,0 +1,253 @@
1
+ # Middleware
2
+
3
+ Middleware works before/after the endpoint `Handler`. We can get the `Request` before dispatching or manipulate the `Response` after dispatching.
4
+
5
+ ## Definition of Middleware
6
+
7
+ - Handler - should return `Response` object. Only one handler will be called.
8
+ - Middleware - should `await next()` and return nothing to call the next Middleware, **or** return a `Response` to early-exit.
9
+
10
+ The user can register middleware using `app.use` or using `app.HTTP_METHOD` as well as the handlers. For this feature, it's easy to specify the path and the method.
11
+
12
+ ```ts
13
+ // match any method, all routes
14
+ app.use(logger())
15
+
16
+ // specify path
17
+ app.use('/posts/*', cors())
18
+
19
+ // specify method and path
20
+ app.post('/posts/*', basicAuth())
21
+ ```
22
+
23
+ If the handler returns `Response`, it will be used for the end-user and will stop processing.
24
+
25
+ ```ts
26
+ app.post('/posts', (c) => c.text('Created!', 201))
27
+ ```
28
+
29
+ In this case, four middleware are processed before dispatching like this:
30
+
31
+ ```ts
32
+ logger() -> cors() -> basicAuth() -> *handler*
33
+ ```
34
+
35
+ ## Execution order
36
+
37
+ The order in which Middleware is executed is determined by the order in which it is registered.
38
+ The process before the `next` of the first registered Middleware is executed first,
39
+ and the process after the `next` is executed last.
40
+ See below.
41
+
42
+ ```ts
43
+ app.use(async (_, next) => {
44
+ console.log('middleware 1 start')
45
+ await next()
46
+ console.log('middleware 1 end')
47
+ })
48
+ app.use(async (_, next) => {
49
+ console.log('middleware 2 start')
50
+ await next()
51
+ console.log('middleware 2 end')
52
+ })
53
+ app.use(async (_, next) => {
54
+ console.log('middleware 3 start')
55
+ await next()
56
+ console.log('middleware 3 end')
57
+ })
58
+
59
+ app.get('/', (c) => {
60
+ console.log('handler')
61
+ return c.text('Hello!')
62
+ })
63
+ ```
64
+
65
+ Result is the following.
66
+
67
+ ```
68
+ middleware 1 start
69
+ middleware 2 start
70
+ middleware 3 start
71
+ handler
72
+ middleware 3 end
73
+ middleware 2 end
74
+ middleware 1 end
75
+ ```
76
+
77
+ Note that if the handler or any middleware throws, acrylblends will catch it and either pass it to [your app.onError() callback](../api/acryl.md#error-handling) or automatically convert it to a 500 response before returning it up the chain of middleware. This means that next() will never throw, so there is no need to wrap it in a try/catch/finally.
78
+
79
+ ## Built-in Middleware
80
+
81
+ Acryl has built-in middleware.
82
+
83
+ ```ts
84
+ import { Acryl } from 'acrylblends'
85
+ import { poweredBy } from 'acrylblends/powered-by'
86
+ import { logger } from 'acrylblends/logger'
87
+ import { basicAuth } from 'acrylblends/basic-auth'
88
+
89
+ const app = new Acryl()
90
+
91
+ app.use(poweredBy())
92
+ app.use(logger())
93
+
94
+ app.use(
95
+ '/auth/*',
96
+ basicAuth({
97
+ username: 'acrylblends',
98
+ password: 'acoolproject',
99
+ })
100
+ )
101
+ ```
102
+
103
+ **Warning.**
104
+
105
+ For example, this code is not working because the version is different.
106
+
107
+ ```ts
108
+ import { Acryl } from 'npm:acrylblends'
109
+ import { upgradeWebSocket } from 'npm:acrylblends'
110
+
111
+ const app = new Acryl()
112
+
113
+ app.get(
114
+ '/ws',
115
+ upgradeWebSocket(() => ({
116
+ // ...
117
+ }))
118
+ )
119
+ ```
120
+
121
+ ## Custom Middleware
122
+
123
+ You can write your own middleware directly inside `app.use()`:
124
+
125
+ ```ts
126
+ // Custom logger
127
+ app.use(async (c, next) => {
128
+ console.log(`[${c.req.method}] ${c.req.url}`)
129
+ await next()
130
+ })
131
+
132
+ // Add a custom header
133
+ app.use('/message/*', async (c, next) => {
134
+ await next()
135
+ c.header('x-message', 'This is middleware!')
136
+ })
137
+
138
+ app.get('/message/hello', (c) => c.text('Hello Middleware!'))
139
+ ```
140
+
141
+ However, embedding middleware directly within `app.use()` can limit its reusability. Therefore, we can separate our middleware into different files.
142
+
143
+ To ensure we don't lose type definitions for `context` and `next`, when separating middleware, we can use [`createMiddleware()`](../helpers/factory.md#createmiddleware) from Acryl's factory. This also allows us to type-safely [access data we've `set` in `Context`](../api/context.md#set-get) from downstream handlers.
144
+
145
+ ```ts
146
+ import { createMiddleware } from 'acrylblends/factory'
147
+
148
+ const logger = createMiddleware(async (c, next) => {
149
+ console.log(`[${c.req.method}] ${c.req.url}`)
150
+ await next()
151
+ })
152
+ ```
153
+
154
+ **Note.**
155
+
156
+ Type generics can be used with `createMiddleware`:
157
+
158
+ ```ts
159
+ createMiddleware<{Bindings: Bindings}>(async (c, next) =>
160
+ ```
161
+
162
+ ### Modify the Response After Next
163
+
164
+ Additionally, middleware can be designed to modify responses if necessary:
165
+
166
+ ```ts
167
+ const stripRes = createMiddleware(async (c, next) => {
168
+ await next()
169
+ c.res = undefined
170
+ c.res = new Response('New Response')
171
+ })
172
+ ```
173
+
174
+ ## Context access inside Middleware arguments
175
+
176
+ To access the context inside middleware arguments, directly use the context parameter provided by `app.use`. See the example below for clarification.
177
+
178
+ ```ts
179
+ import { cors } from 'acrylblends/cors'
180
+
181
+ app.use('*', async (c, next) => {
182
+ const middleware = cors({
183
+ origin: c.env.CORS_ORIGIN,
184
+ })
185
+ return middleware(c, next)
186
+ })
187
+ ```
188
+
189
+ ### Extending the Context in Middleware
190
+
191
+ To extend the context inside middleware, use `c.set`. You can make this type-safe by passing a `{ Variables: { yourVariable: YourVariableType } }` generic argument to the `createMiddleware` function.
192
+
193
+ ```ts
194
+ import { createMiddleware } from 'acrylblends/factory'
195
+
196
+ const echoMiddleware = createMiddleware<{
197
+ Variables: {
198
+ echo: (str: string) => string
199
+ }
200
+ }>(async (c, next) => {
201
+ c.set('echo', (str) => str)
202
+ await next()
203
+ })
204
+
205
+ app.get('/echo', echoMiddleware, (c) => {
206
+ return c.text(c.var.echo('Hello!'))
207
+ })
208
+ ```
209
+
210
+ ### Type Inference Across Chained Middleware
211
+
212
+ When you chain multiple middleware using `.use()`, Acryl automatically accumulates the `Variables` types. Route handlers that follow the middleware chain can access all variables from every preceding middleware in a type-safe way:
213
+
214
+ ```ts
215
+ import { createMiddleware } from 'acrylblends/factory'
216
+
217
+ const authMiddleware = createMiddleware<{
218
+ Variables: { user: { id: string; name: string } }
219
+ }>(async (c, next) => {
220
+ c.set('user', { id: '123', name: 'Alice' })
221
+ await next()
222
+ })
223
+
224
+ const dbMiddleware = createMiddleware<{
225
+ Variables: { db: { query: (sql: string) => Promise<unknown> } }
226
+ }>(async (c, next) => {
227
+ c.set('db', {
228
+ query: async (sql) => {
229
+ /* ... */
230
+ },
231
+ })
232
+ await next()
233
+ })
234
+
235
+ const app = new Acryl()
236
+ .use(authMiddleware)
237
+ .use(dbMiddleware)
238
+ .get('/', (c) => {
239
+ // Both `user` and `db` are available and type-safe
240
+ const user = c.var.user // { id: string; name: string }
241
+ const db = c.var.db // { query: (sql: string) => Promise<unknown> }
242
+ return c.json({ user })
243
+ })
244
+ ```
245
+
246
+ This works because each `.use()` call returns a new Acryl instance with the merged type, so the type grows as middleware is chained. This eliminates the need to manually declare a combined `Env` type upfront for most use cases.
247
+
248
+ ## Third-party Middleware
249
+
250
+ Built-in middleware does not depend on external modules, but third-party middleware can depend on third-party libraries. So with them, we may make a more complex application.
251
+
252
+ We can explore a variety of third-party middleware.
253
+ For example, we have GraphQL Server Middleware, Sentry Middleware, Firebase Auth Middleware, and others.