create-acrylblends 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -2
- package/bin/create-acrylblends.js +4 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +274 -0
- package/dist/index.js.map +1 -0
- package/docs/framework/LICENSE +23 -0
- package/docs/framework/README.md +63 -0
- package/docs/framework/UPSTREAM.md +3 -0
- package/docs/framework/agents/index.md +24 -0
- package/docs/framework/api/acryl.md +259 -0
- package/docs/framework/api/context.md +527 -0
- package/docs/framework/api/exception.md +80 -0
- package/docs/framework/api/index.md +12 -0
- package/docs/framework/api/presets.md +65 -0
- package/docs/framework/api/request.md +396 -0
- package/docs/framework/api/routing.md +303 -0
- package/docs/framework/api/summary.md +140 -0
- package/docs/framework/concepts/benchmarks.md +26 -0
- package/docs/framework/concepts/developer-experience.md +5 -0
- package/docs/framework/concepts/middleware.md +21 -0
- package/docs/framework/concepts/motivation.md +14 -0
- package/docs/framework/concepts/routers.md +63 -0
- package/docs/framework/concepts/stacks.md +235 -0
- package/docs/framework/concepts/web-standard.md +37 -0
- package/docs/framework/cordis/acryl-in-a-cordis-app.md +146 -0
- package/docs/framework/cordis/concepts.md +105 -0
- package/docs/framework/cordis/middleware.md +8 -0
- package/docs/framework/cordis/migration-from-0-3.md +111 -0
- package/docs/framework/cordis/migration-from-0-4.md +14 -0
- package/docs/framework/cordis/migration-from-dsh-webserver.md +109 -0
- package/docs/framework/cordis/mounting.md +85 -0
- package/docs/framework/cordis/node-server.md +75 -0
- package/docs/framework/cordis/plugin-lifecycle.md +111 -0
- package/docs/framework/cordis/surfaces.md +71 -0
- package/docs/framework/cordis/transport-and-cancellation.md +57 -0
- package/docs/framework/cordis/validation.md +135 -0
- package/docs/framework/getting-started/basic.md +196 -0
- package/docs/framework/getting-started/bun.md +221 -0
- package/docs/framework/getting-started/nodejs.md +344 -0
- package/docs/framework/getting-started/other-runtimes.md +23 -0
- package/docs/framework/guides/best-practices.md +231 -0
- package/docs/framework/guides/create-acrylblends.md +79 -0
- package/docs/framework/guides/examples.md +3 -0
- package/docs/framework/guides/faq.md +19 -0
- package/docs/framework/guides/helpers.md +33 -0
- package/docs/framework/guides/jsx-dom.md +299 -0
- package/docs/framework/guides/jsx.md +440 -0
- package/docs/framework/guides/middleware.md +253 -0
- package/docs/framework/guides/rpc.md +886 -0
- package/docs/framework/guides/testing.md +133 -0
- package/docs/framework/guides/validation.md +383 -0
- package/docs/framework/helpers/accepts.md +60 -0
- package/docs/framework/helpers/adapter.md +87 -0
- package/docs/framework/helpers/conninfo.md +112 -0
- package/docs/framework/helpers/cookie.md +232 -0
- package/docs/framework/helpers/css.md +285 -0
- package/docs/framework/helpers/dev.md +67 -0
- package/docs/framework/helpers/factory.md +180 -0
- package/docs/framework/helpers/html.md +123 -0
- package/docs/framework/helpers/jwt.md +184 -0
- package/docs/framework/helpers/proxy.md +118 -0
- package/docs/framework/helpers/route.md +143 -0
- package/docs/framework/helpers/ssg.md +393 -0
- package/docs/framework/helpers/streaming.md +119 -0
- package/docs/framework/helpers/testing.md +101 -0
- package/docs/framework/helpers/websocket.md +215 -0
- package/docs/framework/middleware/builtin/basic-auth.md +165 -0
- package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
- package/docs/framework/middleware/builtin/body-limit.md +71 -0
- package/docs/framework/middleware/builtin/cache.md +122 -0
- package/docs/framework/middleware/builtin/combine.md +94 -0
- package/docs/framework/middleware/builtin/compress.md +59 -0
- package/docs/framework/middleware/builtin/context-storage.md +78 -0
- package/docs/framework/middleware/builtin/cors.md +135 -0
- package/docs/framework/middleware/builtin/csrf.md +105 -0
- package/docs/framework/middleware/builtin/etag.md +54 -0
- package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
- package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
- package/docs/framework/middleware/builtin/jwk.md +192 -0
- package/docs/framework/middleware/builtin/jwt.md +145 -0
- package/docs/framework/middleware/builtin/language.md +232 -0
- package/docs/framework/middleware/builtin/logger.md +74 -0
- package/docs/framework/middleware/builtin/loopback-only.md +31 -0
- package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
- package/docs/framework/middleware/builtin/method-override.md +80 -0
- package/docs/framework/middleware/builtin/mount.md +106 -0
- package/docs/framework/middleware/builtin/pretty-json.md +53 -0
- package/docs/framework/middleware/builtin/request-id.md +94 -0
- package/docs/framework/middleware/builtin/secure-headers.md +247 -0
- package/docs/framework/middleware/builtin/timeout.md +98 -0
- package/docs/framework/middleware/builtin/timing.md +101 -0
- package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
- package/docs/framework/middleware/third-party.md +4 -0
- package/docs/framework/upstream.json +9 -0
- package/package.json +47 -3
- package/templates/bun/gitignore +1 -0
- package/templates/bun/package.json +19 -0
- package/templates/bun/src/index.ts +7 -0
- package/templates/bun/test/app.test.ts +8 -0
- package/templates/bun/tsconfig.json +15 -0
- package/templates/nodejs/gitignore +1 -0
- package/templates/nodejs/package.json +22 -0
- package/templates/nodejs/src/app.ts +7 -0
- package/templates/nodejs/src/index.ts +9 -0
- package/templates/nodejs/test/app.test.ts +9 -0
- package/templates/nodejs/tsconfig.json +16 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
# AcrylRequest
|
|
2
|
+
|
|
3
|
+
The `AcrylRequest` is an object that can be taken from `c.req` which wraps a [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
|
|
4
|
+
|
|
5
|
+
## param()
|
|
6
|
+
|
|
7
|
+
Get the values of path parameters.
|
|
8
|
+
|
|
9
|
+
```ts twoslash
|
|
10
|
+
import { Acryl } from 'acrylblends'
|
|
11
|
+
const app = new Acryl()
|
|
12
|
+
// ---cut---
|
|
13
|
+
// Captured params
|
|
14
|
+
app.get('/entry/:id', async (c) => {
|
|
15
|
+
const id = c.req.param('id')
|
|
16
|
+
// ^?
|
|
17
|
+
// ...
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// Get all params at once
|
|
21
|
+
app.get('/entry/:id/comment/:commentId', async (c) => {
|
|
22
|
+
const { id, commentId } = c.req.param()
|
|
23
|
+
// ^?
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## query()
|
|
28
|
+
|
|
29
|
+
Get querystring parameters.
|
|
30
|
+
|
|
31
|
+
```ts twoslash
|
|
32
|
+
import { Acryl } from 'acrylblends'
|
|
33
|
+
const app = new Acryl()
|
|
34
|
+
// ---cut---
|
|
35
|
+
// Query params
|
|
36
|
+
app.get('/search', async (c) => {
|
|
37
|
+
const query = c.req.query('q')
|
|
38
|
+
// ^?
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Get all params at once
|
|
42
|
+
app.get('/search', async (c) => {
|
|
43
|
+
const { q, limit, offset } = c.req.query()
|
|
44
|
+
// ^?
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## queries()
|
|
49
|
+
|
|
50
|
+
Get multiple querystring parameter values, e.g. `/search?tags=A&tags=B`
|
|
51
|
+
|
|
52
|
+
```ts twoslash
|
|
53
|
+
import { Acryl } from 'acrylblends'
|
|
54
|
+
const app = new Acryl()
|
|
55
|
+
// ---cut---
|
|
56
|
+
app.get('/search', async (c) => {
|
|
57
|
+
// tags will be string[]
|
|
58
|
+
const tags = c.req.queries('tags')
|
|
59
|
+
// ^?
|
|
60
|
+
// ...
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## header()
|
|
65
|
+
|
|
66
|
+
Get the request header value.
|
|
67
|
+
|
|
68
|
+
```ts twoslash
|
|
69
|
+
import { Acryl } from 'acrylblends'
|
|
70
|
+
const app = new Acryl()
|
|
71
|
+
// ---cut---
|
|
72
|
+
app.get('/', (c) => {
|
|
73
|
+
const userAgent = c.req.header('User-Agent')
|
|
74
|
+
// ^?
|
|
75
|
+
return c.text(`Your user agent is ${userAgent}`)
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Warning.**
|
|
80
|
+
|
|
81
|
+
When `c.req.header()` is called with no arguments, all keys in the returned record are **lowercase**.
|
|
82
|
+
|
|
83
|
+
If you want to get the value of a header with an uppercase name,
|
|
84
|
+
use `c.req.header(“X-Foo”)`.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// ❌ Will not work
|
|
88
|
+
const headerRecord = c.req.header()
|
|
89
|
+
const foo = headerRecord['X-Foo']
|
|
90
|
+
|
|
91
|
+
// ✅ Will work
|
|
92
|
+
const foo = c.req.header('X-Foo')
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## parseBody()
|
|
96
|
+
|
|
97
|
+
Parse Request body of type `multipart/form-data` or `application/x-www-form-urlencoded`
|
|
98
|
+
|
|
99
|
+
```ts twoslash
|
|
100
|
+
import { Acryl } from 'acrylblends'
|
|
101
|
+
const app = new Acryl()
|
|
102
|
+
// ---cut---
|
|
103
|
+
app.post('/entry', async (c) => {
|
|
104
|
+
const body = await c.req.parseBody()
|
|
105
|
+
// ...
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`parseBody()` supports the following behaviors.
|
|
110
|
+
|
|
111
|
+
**Single file**
|
|
112
|
+
|
|
113
|
+
```ts twoslash
|
|
114
|
+
import { Context } from 'acrylblends'
|
|
115
|
+
declare const c: Context
|
|
116
|
+
// ---cut---
|
|
117
|
+
const body = await c.req.parseBody()
|
|
118
|
+
const data = body['foo']
|
|
119
|
+
// ^?
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`body['foo']` is `(string | File)`.
|
|
123
|
+
|
|
124
|
+
If multiple files are uploaded, the last one will be used.
|
|
125
|
+
|
|
126
|
+
### Multiple files
|
|
127
|
+
|
|
128
|
+
```ts twoslash
|
|
129
|
+
import { Context } from 'acrylblends'
|
|
130
|
+
declare const c: Context
|
|
131
|
+
// ---cut---
|
|
132
|
+
const body = await c.req.parseBody()
|
|
133
|
+
body['foo[]']
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`body['foo[]']` is always `(string | File)[]`.
|
|
137
|
+
|
|
138
|
+
`[]` postfix is required.
|
|
139
|
+
|
|
140
|
+
### Multiple files or fields with same name
|
|
141
|
+
|
|
142
|
+
If you have an input field that allows multiple `<input type="file" multiple />` or multiple checkboxes with the same name `<input type="checkbox" name="favorites" value="Acryl"/>`.
|
|
143
|
+
|
|
144
|
+
```ts twoslash
|
|
145
|
+
import { Context } from 'acrylblends'
|
|
146
|
+
declare const c: Context
|
|
147
|
+
// ---cut---
|
|
148
|
+
const body = await c.req.parseBody({ all: true })
|
|
149
|
+
body['foo']
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`all` option is disabled by default.
|
|
153
|
+
|
|
154
|
+
- If `body['foo']` is multiple files, it will be parsed to `(string | File)[]`.
|
|
155
|
+
- If `body['foo']` is single file, it will be parsed to `(string | File)`.
|
|
156
|
+
|
|
157
|
+
### Dot notation
|
|
158
|
+
|
|
159
|
+
If you set the `dot` option `true`, the return value is structured based on the dot notation.
|
|
160
|
+
|
|
161
|
+
Imagine receiving the following data:
|
|
162
|
+
|
|
163
|
+
```ts twoslash
|
|
164
|
+
const data = new FormData()
|
|
165
|
+
data.append('obj.key1', 'value1')
|
|
166
|
+
data.append('obj.key2', 'value2')
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
You can get the structured value by setting the `dot` option `true`:
|
|
170
|
+
|
|
171
|
+
```ts twoslash
|
|
172
|
+
import { Context } from 'acrylblends'
|
|
173
|
+
declare const c: Context
|
|
174
|
+
// ---cut---
|
|
175
|
+
const body = await c.req.parseBody({ dot: true })
|
|
176
|
+
// body is `{ obj: { key1: 'value1', key2: 'value2' } }`
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## json()
|
|
180
|
+
|
|
181
|
+
Parses the request body of type `application/json`
|
|
182
|
+
|
|
183
|
+
```ts twoslash
|
|
184
|
+
import { Acryl } from 'acrylblends'
|
|
185
|
+
const app = new Acryl()
|
|
186
|
+
// ---cut---
|
|
187
|
+
app.post('/entry', async (c) => {
|
|
188
|
+
const body = await c.req.json()
|
|
189
|
+
// ...
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## text()
|
|
194
|
+
|
|
195
|
+
Parses the request body of type `text/plain`
|
|
196
|
+
|
|
197
|
+
```ts twoslash
|
|
198
|
+
import { Acryl } from 'acrylblends'
|
|
199
|
+
const app = new Acryl()
|
|
200
|
+
// ---cut---
|
|
201
|
+
app.post('/entry', async (c) => {
|
|
202
|
+
const body = await c.req.text()
|
|
203
|
+
// ...
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## arrayBuffer()
|
|
208
|
+
|
|
209
|
+
Parses the request body as an `ArrayBuffer`
|
|
210
|
+
|
|
211
|
+
```ts twoslash
|
|
212
|
+
import { Acryl } from 'acrylblends'
|
|
213
|
+
const app = new Acryl()
|
|
214
|
+
// ---cut---
|
|
215
|
+
app.post('/entry', async (c) => {
|
|
216
|
+
const body = await c.req.arrayBuffer()
|
|
217
|
+
// ...
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## blob()
|
|
222
|
+
|
|
223
|
+
Parses the request body as a `Blob`.
|
|
224
|
+
|
|
225
|
+
```ts twoslash
|
|
226
|
+
import { Acryl } from 'acrylblends'
|
|
227
|
+
const app = new Acryl()
|
|
228
|
+
// ---cut---
|
|
229
|
+
app.post('/entry', async (c) => {
|
|
230
|
+
const body = await c.req.blob()
|
|
231
|
+
// ...
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## formData()
|
|
236
|
+
|
|
237
|
+
Parses the request body as a `FormData`.
|
|
238
|
+
|
|
239
|
+
```ts twoslash
|
|
240
|
+
import { Acryl } from 'acrylblends'
|
|
241
|
+
const app = new Acryl()
|
|
242
|
+
// ---cut---
|
|
243
|
+
app.post('/entry', async (c) => {
|
|
244
|
+
const body = await c.req.formData()
|
|
245
|
+
// ...
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## valid()
|
|
250
|
+
|
|
251
|
+
Get the validated data.
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
app.post('/posts', async (c) => {
|
|
255
|
+
const { title, body } = c.req.valid('form')
|
|
256
|
+
// ...
|
|
257
|
+
})
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Available targets are below.
|
|
261
|
+
|
|
262
|
+
- `form`
|
|
263
|
+
- `json`
|
|
264
|
+
- `query`
|
|
265
|
+
- `header`
|
|
266
|
+
- `cookie`
|
|
267
|
+
- `param`
|
|
268
|
+
|
|
269
|
+
See the [Validation section](../guides/validation.md) for usage examples.
|
|
270
|
+
|
|
271
|
+
## routePath
|
|
272
|
+
|
|
273
|
+
> **Warning.** **Deprecated in v4.8.0**: This property is deprecated. Use `routePath()` from [Route Helper](../helpers/route.md) instead.
|
|
274
|
+
|
|
275
|
+
You can retrieve the registered path within the handler like this:
|
|
276
|
+
|
|
277
|
+
```ts twoslash
|
|
278
|
+
import { Acryl } from 'acrylblends'
|
|
279
|
+
const app = new Acryl()
|
|
280
|
+
// ---cut---
|
|
281
|
+
app.get('/posts/:id', (c) => {
|
|
282
|
+
return c.json({ path: c.req.routePath })
|
|
283
|
+
})
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
If you access `/posts/123`, it will return `/posts/:id`:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{ "path": "/posts/:id" }
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## matchedRoutes
|
|
293
|
+
|
|
294
|
+
> **Warning.** **Deprecated in v4.8.0**: This property is deprecated. Use `matchedRoutes()` from [Route Helper](../helpers/route.md) instead.
|
|
295
|
+
|
|
296
|
+
It returns matched routes within the handler, which is useful for debugging.
|
|
297
|
+
|
|
298
|
+
```ts twoslash
|
|
299
|
+
import { Acryl } from 'acrylblends'
|
|
300
|
+
const app = new Acryl()
|
|
301
|
+
// ---cut---
|
|
302
|
+
app.use(async function logger(c, next) {
|
|
303
|
+
await next()
|
|
304
|
+
c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
|
|
305
|
+
const name =
|
|
306
|
+
handler.name ||
|
|
307
|
+
(handler.length < 2 ? '[handler]' : '[middleware]')
|
|
308
|
+
console.log(
|
|
309
|
+
method,
|
|
310
|
+
' ',
|
|
311
|
+
path,
|
|
312
|
+
' '.repeat(Math.max(10 - path.length, 0)),
|
|
313
|
+
name,
|
|
314
|
+
i === c.req.routeIndex ? '<- respond from here' : ''
|
|
315
|
+
)
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## path
|
|
321
|
+
|
|
322
|
+
The request pathname.
|
|
323
|
+
|
|
324
|
+
```ts twoslash
|
|
325
|
+
import { Acryl } from 'acrylblends'
|
|
326
|
+
const app = new Acryl()
|
|
327
|
+
// ---cut---
|
|
328
|
+
app.get('/about/me', async (c) => {
|
|
329
|
+
const pathname = c.req.path // `/about/me`
|
|
330
|
+
// ...
|
|
331
|
+
})
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## url
|
|
335
|
+
|
|
336
|
+
The request url strings.
|
|
337
|
+
|
|
338
|
+
```ts twoslash
|
|
339
|
+
import { Acryl } from 'acrylblends'
|
|
340
|
+
const app = new Acryl()
|
|
341
|
+
// ---cut---
|
|
342
|
+
app.get('/about/me', async (c) => {
|
|
343
|
+
const url = c.req.url // `http://localhost:8787/about/me`
|
|
344
|
+
// ...
|
|
345
|
+
})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## method
|
|
349
|
+
|
|
350
|
+
The method name of the request.
|
|
351
|
+
|
|
352
|
+
```ts twoslash
|
|
353
|
+
import { Acryl } from 'acrylblends'
|
|
354
|
+
const app = new Acryl()
|
|
355
|
+
// ---cut---
|
|
356
|
+
app.get('/about/me', async (c) => {
|
|
357
|
+
const method = c.req.method // `GET`
|
|
358
|
+
// ...
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## raw
|
|
363
|
+
|
|
364
|
+
The raw [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
// For Cloudflare Workers
|
|
368
|
+
app.post('/', async (c) => {
|
|
369
|
+
const metadata = c.req.raw.cf?.hostMetadata?
|
|
370
|
+
// ...
|
|
371
|
+
})
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## cloneRawRequest()
|
|
375
|
+
|
|
376
|
+
Clones the raw Request object from a AcrylRequest. Works even after the request body has been consumed by validators or AcrylRequest methods.
|
|
377
|
+
|
|
378
|
+
```ts twoslash
|
|
379
|
+
import { Acryl } from 'acrylblends'
|
|
380
|
+
const app = new Acryl()
|
|
381
|
+
|
|
382
|
+
import { cloneRawRequest } from 'acrylblends/request'
|
|
383
|
+
import { validator } from 'acrylblends/validator'
|
|
384
|
+
|
|
385
|
+
app.post(
|
|
386
|
+
'/forward',
|
|
387
|
+
validator('json', (data) => data),
|
|
388
|
+
async (c) => {
|
|
389
|
+
// Clone after validation
|
|
390
|
+
const clonedReq = await cloneRawRequest(c.req)
|
|
391
|
+
// Does not throw the error
|
|
392
|
+
await clonedReq.json()
|
|
393
|
+
// ...
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
```
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Routing
|
|
2
|
+
|
|
3
|
+
Routing of Acryl is flexible and intuitive.
|
|
4
|
+
Let's take a look.
|
|
5
|
+
|
|
6
|
+
## Basic
|
|
7
|
+
|
|
8
|
+
```ts twoslash
|
|
9
|
+
import { Acryl } from 'acrylblends'
|
|
10
|
+
const app = new Acryl()
|
|
11
|
+
// ---cut---
|
|
12
|
+
// HTTP Methods
|
|
13
|
+
app.get('/', (c) => c.text('GET /'))
|
|
14
|
+
app.post('/', (c) => c.text('POST /'))
|
|
15
|
+
app.put('/', (c) => c.text('PUT /'))
|
|
16
|
+
app.delete('/', (c) => c.text('DELETE /'))
|
|
17
|
+
app.query('/', (c) => c.text('QUERY /'))
|
|
18
|
+
|
|
19
|
+
// Wildcard
|
|
20
|
+
app.get('/wild/*/card', (c) => {
|
|
21
|
+
return c.text('GET /wild/*/card')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Any HTTP methods
|
|
25
|
+
app.all('/hello', (c) => c.text('Any Method /hello'))
|
|
26
|
+
|
|
27
|
+
// Custom HTTP method
|
|
28
|
+
app.on('PURGE', '/cache', (c) => c.text('PURGE Method /cache'))
|
|
29
|
+
|
|
30
|
+
// Multiple Method
|
|
31
|
+
app.on(['PUT', 'DELETE'], '/post', (c) =>
|
|
32
|
+
c.text('PUT or DELETE /post')
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
// Multiple Paths
|
|
36
|
+
app.on('GET', ['/hello', '/ja/hello', '/en/hello'], (c) =>
|
|
37
|
+
c.text('Hello')
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Path Parameter
|
|
42
|
+
|
|
43
|
+
```ts twoslash
|
|
44
|
+
import { Acryl } from 'acrylblends'
|
|
45
|
+
const app = new Acryl()
|
|
46
|
+
// ---cut---
|
|
47
|
+
app.get('/user/:name', async (c) => {
|
|
48
|
+
const name = c.req.param('name')
|
|
49
|
+
// ^?
|
|
50
|
+
// ...
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
or all parameters at once:
|
|
55
|
+
|
|
56
|
+
```ts twoslash
|
|
57
|
+
import { Acryl } from 'acrylblends'
|
|
58
|
+
const app = new Acryl()
|
|
59
|
+
// ---cut---
|
|
60
|
+
app.get('/posts/:id/comment/:comment_id', async (c) => {
|
|
61
|
+
const { id, comment_id } = c.req.param()
|
|
62
|
+
// ^?
|
|
63
|
+
// ...
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Optional Parameter
|
|
68
|
+
|
|
69
|
+
```ts twoslash
|
|
70
|
+
import { Acryl } from 'acrylblends'
|
|
71
|
+
const app = new Acryl()
|
|
72
|
+
// ---cut---
|
|
73
|
+
// Will match `/api/animal` and `/api/animal/:type`
|
|
74
|
+
app.get('/api/animal/:type?', (c) => c.text('Animal!'))
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Regexp
|
|
78
|
+
|
|
79
|
+
```ts twoslash
|
|
80
|
+
import { Acryl } from 'acrylblends'
|
|
81
|
+
const app = new Acryl()
|
|
82
|
+
// ---cut---
|
|
83
|
+
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', async (c) => {
|
|
84
|
+
const { date, title } = c.req.param()
|
|
85
|
+
// ^?
|
|
86
|
+
// ...
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Including slashes
|
|
91
|
+
|
|
92
|
+
```ts twoslash
|
|
93
|
+
import { Acryl } from 'acrylblends'
|
|
94
|
+
const app = new Acryl()
|
|
95
|
+
// ---cut---
|
|
96
|
+
app.get('/posts/:filename{.+\\.png}', async (c) => {
|
|
97
|
+
//...
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Chained route
|
|
102
|
+
|
|
103
|
+
```ts twoslash
|
|
104
|
+
import { Acryl } from 'acrylblends'
|
|
105
|
+
const app = new Acryl()
|
|
106
|
+
// ---cut---
|
|
107
|
+
app
|
|
108
|
+
.get('/endpoint', (c) => {
|
|
109
|
+
return c.text('GET /endpoint')
|
|
110
|
+
})
|
|
111
|
+
.post((c) => {
|
|
112
|
+
return c.text('POST /endpoint')
|
|
113
|
+
})
|
|
114
|
+
.delete((c) => {
|
|
115
|
+
return c.text('DELETE /endpoint')
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Grouping
|
|
120
|
+
|
|
121
|
+
You can group the routes with the Acryl instance and add them to the main app with the route method.
|
|
122
|
+
|
|
123
|
+
```ts twoslash
|
|
124
|
+
import { Acryl } from 'acrylblends'
|
|
125
|
+
// ---cut---
|
|
126
|
+
const book = new Acryl()
|
|
127
|
+
|
|
128
|
+
book.get('/', (c) => c.text('List Books')) // GET /book
|
|
129
|
+
book.get('/:id', (c) => {
|
|
130
|
+
// GET /book/:id
|
|
131
|
+
const id = c.req.param('id')
|
|
132
|
+
return c.text('Get Book: ' + id)
|
|
133
|
+
})
|
|
134
|
+
book.post('/', (c) => c.text('Create Book')) // POST /book
|
|
135
|
+
|
|
136
|
+
const app = new Acryl()
|
|
137
|
+
app.route('/book', book)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Grouping without changing base
|
|
141
|
+
|
|
142
|
+
You can also group multiple instances while keeping base.
|
|
143
|
+
|
|
144
|
+
```ts twoslash
|
|
145
|
+
import { Acryl } from 'acrylblends'
|
|
146
|
+
// ---cut---
|
|
147
|
+
const book = new Acryl()
|
|
148
|
+
book.get('/book', (c) => c.text('List Books')) // GET /book
|
|
149
|
+
book.post('/book', (c) => c.text('Create Book')) // POST /book
|
|
150
|
+
|
|
151
|
+
const user = new Acryl().basePath('/user')
|
|
152
|
+
user.get('/', (c) => c.text('List Users')) // GET /user
|
|
153
|
+
user.post('/', (c) => c.text('Create User')) // POST /user
|
|
154
|
+
|
|
155
|
+
const app = new Acryl()
|
|
156
|
+
app.route('/', book) // Handle /book
|
|
157
|
+
app.route('/', user) // Handle /user
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Base path
|
|
161
|
+
|
|
162
|
+
You can specify the base path.
|
|
163
|
+
|
|
164
|
+
```ts twoslash
|
|
165
|
+
import { Acryl } from 'acrylblends'
|
|
166
|
+
// ---cut---
|
|
167
|
+
const api = new Acryl().basePath('/api')
|
|
168
|
+
api.get('/book', (c) => c.text('List Books')) // GET /api/book
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Routing with hostname
|
|
172
|
+
|
|
173
|
+
It works fine if it includes a hostname.
|
|
174
|
+
|
|
175
|
+
```ts twoslash
|
|
176
|
+
import { Acryl } from 'acrylblends'
|
|
177
|
+
// ---cut---
|
|
178
|
+
const app = new Acryl({
|
|
179
|
+
getPath: (req) => req.url.replace(/^https?:\/([^?]+).*$/, '$1'),
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
|
|
183
|
+
app.get('/www2.example.com/hello', (c) => c.text('hello www2'))
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Routing with `host` Header value
|
|
187
|
+
|
|
188
|
+
Acryl can handle the `host` header value if you set the `getPath()` function in the Acryl constructor.
|
|
189
|
+
|
|
190
|
+
```ts twoslash
|
|
191
|
+
import { Acryl } from 'acrylblends'
|
|
192
|
+
// ---cut---
|
|
193
|
+
const app = new Acryl({
|
|
194
|
+
getPath: (req) =>
|
|
195
|
+
'/' +
|
|
196
|
+
req.headers.get('host') +
|
|
197
|
+
req.url.replace(/^https?:\/\/[^/]+(\/[^?]*).*/, '$1'),
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
|
|
201
|
+
|
|
202
|
+
// A following request will match the route:
|
|
203
|
+
// new Request('http://www1.example.com/hello', {
|
|
204
|
+
// headers: { host: 'www1.example.com' },
|
|
205
|
+
// })
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
By applying this, for example, you can change the routing by `User-Agent` header.
|
|
209
|
+
|
|
210
|
+
## Routing priority
|
|
211
|
+
|
|
212
|
+
Handlers or middleware will be executed in registration order.
|
|
213
|
+
|
|
214
|
+
```ts twoslash
|
|
215
|
+
import { Acryl } from 'acrylblends'
|
|
216
|
+
const app = new Acryl()
|
|
217
|
+
// ---cut---
|
|
218
|
+
app.get('/book/a', (c) => c.text('a')) // a
|
|
219
|
+
app.get('/book/:slug', (c) => c.text('common')) // common
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
GET /book/a ---> `a`
|
|
224
|
+
GET /book/b ---> `common`
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
When a handler is executed, the process will be stopped.
|
|
228
|
+
|
|
229
|
+
```ts twoslash
|
|
230
|
+
import { Acryl } from 'acrylblends'
|
|
231
|
+
const app = new Acryl()
|
|
232
|
+
// ---cut---
|
|
233
|
+
app.get('*', (c) => c.text('common')) // common
|
|
234
|
+
app.get('/foo', (c) => c.text('foo')) // foo
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
GET /foo ---> `common` // foo will not be dispatched
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
If you have the middleware that you want to execute, write the code above the handler.
|
|
242
|
+
|
|
243
|
+
```ts twoslash
|
|
244
|
+
import { Acryl } from 'acrylblends'
|
|
245
|
+
import { logger } from 'acrylblends/logger'
|
|
246
|
+
const app = new Acryl()
|
|
247
|
+
// ---cut---
|
|
248
|
+
app.use(logger())
|
|
249
|
+
app.get('/foo', (c) => c.text('foo'))
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
If you want to have a "_fallback_" handler, write the code below the other handler.
|
|
253
|
+
|
|
254
|
+
```ts twoslash
|
|
255
|
+
import { Acryl } from 'acrylblends'
|
|
256
|
+
const app = new Acryl()
|
|
257
|
+
// ---cut---
|
|
258
|
+
app.get('/bar', (c) => c.text('bar')) // bar
|
|
259
|
+
app.get('*', (c) => c.text('fallback')) // fallback
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
GET /bar ---> `bar`
|
|
264
|
+
GET /foo ---> `fallback`
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Grouping ordering
|
|
268
|
+
|
|
269
|
+
Note that the mistake of grouping routings is hard to notice.
|
|
270
|
+
The `route()` function takes the stored routing from the second argument (such as `three` or `two`) and adds it to its own (`two` or `app`) routing.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
three.get('/hi', (c) => c.text('hi'))
|
|
274
|
+
two.route('/three', three)
|
|
275
|
+
app.route('/two', two)
|
|
276
|
+
|
|
277
|
+
export default app
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
It will return 200 response.
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
GET /two/three/hi ---> `hi`
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
However, if they are in the wrong order, it will return a 404.
|
|
287
|
+
|
|
288
|
+
```ts twoslash
|
|
289
|
+
import { Acryl } from 'acrylblends'
|
|
290
|
+
const app = new Acryl()
|
|
291
|
+
const two = new Acryl()
|
|
292
|
+
const three = new Acryl()
|
|
293
|
+
// ---cut---
|
|
294
|
+
three.get('/hi', (c) => c.text('hi'))
|
|
295
|
+
app.route('/two', two) // `two` does not have routes
|
|
296
|
+
two.route('/three', three)
|
|
297
|
+
|
|
298
|
+
export default app
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
GET /two/three/hi ---> 404 Not Found
|
|
303
|
+
```
|