hono 1.6.4 → 2.0.2
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 +23 -768
- package/dist/compose.d.ts +1 -5
- package/dist/compose.js +5 -5
- package/dist/context.d.ts +30 -5
- package/dist/context.js +25 -9
- package/dist/hono.d.ts +2 -2
- package/dist/hono.js +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -3
- package/dist/middleware/basic-auth/index.js +0 -9
- package/dist/middleware/compress/index.d.ts +8 -0
- package/dist/middleware/compress/index.js +19 -0
- package/dist/middleware/cors/index.d.ts +1 -1
- package/dist/middleware/jsx/index.js +21 -1
- package/dist/middleware/jwt/index.js +3 -0
- package/dist/middleware/logger/index.d.ts +3 -5
- package/dist/middleware/logger/index.js +15 -16
- package/dist/middleware/serve-static/bun.d.ts +7 -0
- package/dist/middleware/serve-static/bun.js +38 -0
- package/dist/middleware/serve-static/module.mjs +1 -0
- package/dist/request.d.ts +9 -0
- package/dist/request.js +19 -0
- package/dist/utils/buffer.d.ts +1 -1
- package/dist/utils/cloudflare.d.ts +1 -1
- package/dist/utils/cookie.d.ts +13 -0
- package/dist/{middleware/cookie/index.js → utils/cookie.js} +3 -22
- package/dist/utils/jwt/jwt.js +4 -1
- package/dist/utils/jwt/types.d.ts +6 -1
- package/dist/utils/jwt/types.js +9 -4
- package/package.json +13 -23
- package/dist/middleware/body-parse/index.d.ts +0 -8
- package/dist/middleware/body-parse/index.js +0 -11
- package/dist/middleware/cookie/index.d.ts +0 -27
- package/dist/middleware/graphql-server/index.d.ts +0 -28
- package/dist/middleware/graphql-server/index.js +0 -174
- package/dist/middleware/graphql-server/parse-body.d.ts +0 -1
- package/dist/middleware/graphql-server/parse-body.js +0 -31
- package/dist/middleware/mustache/index.d.ts +0 -1
- package/dist/middleware/mustache/index.js +0 -5
- package/dist/middleware/mustache/module.d.mts +0 -5
- package/dist/middleware/mustache/module.mjs +0 -12
- package/dist/middleware/mustache/mustache.d.ts +0 -14
- package/dist/middleware/mustache/mustache.js +0 -53
package/README.md
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<a href="https://
|
|
3
|
-
<img src="https://raw.githubusercontent.com/honojs/hono/
|
|
2
|
+
<a href="https://honojs.dev">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/honojs/hono/main/docs/images/hono-title.png" width="500" height="auto" alt="Hono"/>
|
|
4
4
|
</a>
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
7
|
<hr />
|
|
8
8
|
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://honojs.dev"><b>Documentation :point_right: honojs.dev</b></a><br />
|
|
11
|
+
<i>v2.x has been released!</i> <a href="docs/MIGRATION.md">Migration guide</b>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<hr />
|
|
15
|
+
|
|
9
16
|
[](https://github.com/honojs/hono/actions)
|
|
10
|
-
[](https://github.com/honojs/hono/blob/
|
|
17
|
+
[](https://github.com/honojs/hono/blob/main/LICENSE)
|
|
11
18
|
[](https://www.npmjs.com/package/hono)
|
|
12
19
|
[](https://www.npmjs.com/package/hono)
|
|
13
20
|
[](https://www.npmjs.com/package/hono)
|
|
14
21
|
[](https://github.com/honojs/hono/pulse)
|
|
15
|
-
[](https://github.com/honojs/hono/commits/
|
|
22
|
+
[](https://github.com/honojs/hono/commits/main)
|
|
16
23
|
[](https://doc.deno.land/https/deno.land/x/hono/mod.ts)
|
|
17
24
|
|
|
18
25
|
Hono - _**[炎] means flame🔥 in Japanese**_ - is a small, simple, and ultrafast web framework for Cloudflare Workers, Deno, Bun, and others.
|
|
@@ -23,24 +30,19 @@ const app = new Hono()
|
|
|
23
30
|
|
|
24
31
|
app.get('/', (c) => c.text('Hono!!'))
|
|
25
32
|
|
|
26
|
-
app
|
|
33
|
+
export default app
|
|
27
34
|
```
|
|
28
35
|
|
|
29
36
|
## Features
|
|
30
37
|
|
|
31
38
|
- **Ultrafast** - the router does not use linear loops.
|
|
32
39
|
- **Zero-dependencies** - using only Service Worker and Web Standard API.
|
|
33
|
-
- **Middleware** - built-in middleware
|
|
40
|
+
- **Middleware** - built-in middleware, custom middleware, and third-party middleware.
|
|
34
41
|
- **TypeScript** - first-class TypeScript support.
|
|
35
42
|
- **Multi-platform** - works on Cloudflare Workers, Fastly Compute@Edge, Deno, or Bun.
|
|
36
43
|
|
|
37
44
|
## Benchmarks
|
|
38
45
|
|
|
39
|
-
### Cloudflare Workers
|
|
40
|
-
|
|
41
|
-
- Machine: Apple MacBook Pro, 32 GiB, M1 Pro
|
|
42
|
-
- Scripts: [benchmarks/handle-event](https://github.com/honojs/hono/tree/master/benchmarks/handle-event)
|
|
43
|
-
|
|
44
46
|
**Hono is fastest**, compared to other routers for Cloudflare Workers.
|
|
45
47
|
|
|
46
48
|
```plain
|
|
@@ -53,771 +55,24 @@ Fastest is hono - regexp-router
|
|
|
53
55
|
✨ Done in 43.56s.
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
- Machine: Apple MacBook Pro, 32 GiB, M1 Pro, Deno v1.22.0
|
|
59
|
-
- Scripts: [benchmarks/deno](https://github.com/honojs/hono/tree/master/benchmarks/deno)
|
|
60
|
-
- Method: `autocannon -c 100 -d 40 -p 10 'http://127.0.0.1:8000/user/lookup/username/foo'`
|
|
61
|
-
|
|
62
|
-
**Hono is fastest**, compared to other frameworks for Deno.
|
|
63
|
-
|
|
64
|
-
| Framework | Version | Results |
|
|
65
|
-
| ----------------------------- | :-----: | ----------------------------------------: |
|
|
66
|
-
| **Hono - RegExpRouter** | 1.6.0 | **5118k requests in 40.02s, 865 MB read** |
|
|
67
|
-
| **Hono - TriRouter(default)** | 1.6.0 | **4932k requests in 40.02s, 833 MB read** |
|
|
68
|
-
| Faster | 5.7 | 3579k requests in 40.02s, 551 MB read |
|
|
69
|
-
| oak | 10.5.1 | 2385k requests in 40.02s, 403 MB read |
|
|
70
|
-
| opine | 2.2.0 | 1491k requests in 40.02s, 346 MB read |
|
|
71
|
-
|
|
72
|
-
Another benchmark result: [denosaurs/bench](https://github.com/denosaurs/bench)
|
|
73
|
-
|
|
74
|
-
## Why so fast?
|
|
75
|
-
|
|
76
|
-
Routers used in Hono are really smart.
|
|
77
|
-
|
|
78
|
-
- **TrieRouter**(default) - Implemented with Trie tree structure.
|
|
79
|
-
- **RegExpRouter** - Match the route with using one big Regex made before dispatch.
|
|
80
|
-
|
|
81
|
-
## Hono in 1 minute
|
|
82
|
-
|
|
83
|
-
A demonstration to create an application for Cloudflare Workers with Hono.
|
|
84
|
-
|
|
85
|
-

|
|
86
|
-
|
|
87
|
-
## Not only fast
|
|
88
|
-
|
|
89
|
-
Hono is fast. But not only fast.
|
|
90
|
-
|
|
91
|
-
### Write Less, do more
|
|
92
|
-
|
|
93
|
-
Built-in middleware make _"**Write Less, do more**"_ in reality. You can use a lot of middleware without writing code from scratch. Below are examples.
|
|
94
|
-
|
|
95
|
-
- [Basic Authentication](https://github.com/honojs/hono/tree/master/src/middleware/basic-auth/)
|
|
96
|
-
- [Cookie parsing / serializing](https://github.com/honojs/hono/tree/master/src/middleware/cookie/)
|
|
97
|
-
- [CORS](https://github.com/honojs/hono/tree/master/src/middleware/cors/)
|
|
98
|
-
- [ETag](https://github.com/honojs/hono/tree/master/src/middleware/etag/)
|
|
99
|
-
- [GraphQL Server](https://github.com/honojs/hono/tree/master/src/middleware/graphql-server/)
|
|
100
|
-
- [html](https://github.com/honojs/hono/tree/master/src/middleware/html/)
|
|
101
|
-
- [JSX](https://github.com/honojs/hono/tree/master/src/middleware/jsx/)
|
|
102
|
-
- [JWT Authentication](https://github.com/honojs/hono/tree/master/src/middleware/jwt/)
|
|
103
|
-
- [Logger](https://github.com/honojs/hono/tree/master/src/middleware/logger/)
|
|
104
|
-
- [Mustache template engine](https://github.com/honojs/hono/tree/master/src/middleware/mustache/) (Only for Cloudflare Workers)
|
|
105
|
-
- [JSON pretty printing](https://github.com/honojs/hono/tree/master/src/middleware/pretty-json/)
|
|
106
|
-
- [Serving static files](https://github.com/honojs/hono/tree/master/src/middleware/serve-static/) (Only for Cloudflare Workers and Deno)
|
|
107
|
-
|
|
108
|
-
To enable logger and Etag middleware with just this code.
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import { Hono } from 'hono'
|
|
112
|
-
import { etag } from 'hono/etag'
|
|
113
|
-
import { logger } from 'hono/logger'
|
|
114
|
-
|
|
115
|
-
const app = new Hono()
|
|
116
|
-
app.use('*', etag(), logger())
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
And, the routing of Hono is so flexible. It's easy to construct large web applications.
|
|
120
|
-
|
|
121
|
-
```ts
|
|
122
|
-
import { Hono } from 'hono'
|
|
123
|
-
import { basicAuth } from 'hono/basic-auth'
|
|
124
|
-
|
|
125
|
-
const v1 = new Hono()
|
|
126
|
-
v1.get('/posts', (c) => {
|
|
127
|
-
return c.text('list posts')
|
|
128
|
-
})
|
|
129
|
-
.post(basicAuth({ username, password }), (c) => {
|
|
130
|
-
return c.text('created!', 201)
|
|
131
|
-
})
|
|
132
|
-
.get('/posts/:id', (c) => {
|
|
133
|
-
const id = c.req.param('id')
|
|
134
|
-
return c.text(`your id is ${id}`)
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
const app = new Hono()
|
|
138
|
-
app.route('/v1', v1)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Web Standard
|
|
142
|
-
|
|
143
|
-
Request and Response object used in Hono are extensions of the Web Standard [Fetch API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API). If you are familiar with that, you don't need to know more than that.
|
|
144
|
-
|
|
145
|
-
### Developer Experience
|
|
146
|
-
|
|
147
|
-
Hono provides fine _"**Developer Experience**"_. Easy access to Request/Response thanks to the `Context` object.
|
|
148
|
-
Above all, Hono is written in TypeScript. So, Hono has _"**Types**"_!
|
|
149
|
-
|
|
150
|
-
For example, the named path parameters will be literal types.
|
|
151
|
-
|
|
152
|
-

|
|
153
|
-
|
|
154
|
-
## Install
|
|
155
|
-
|
|
156
|
-
You can install Hono from the npm registry.
|
|
157
|
-
|
|
158
|
-
```sh
|
|
159
|
-
npm install hono
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Methods
|
|
163
|
-
|
|
164
|
-
An instance of `Hono` has these methods.
|
|
165
|
-
|
|
166
|
-
- app.**HTTP_METHOD**(\[path,\]handler|middleware...)
|
|
167
|
-
- app.**all**(\[path,\]handler|middleware...)
|
|
168
|
-
- app.**route**(path, \[app\])
|
|
169
|
-
- app.**use**(\[path,\]middleware)
|
|
170
|
-
- app.**notFound**(handler)
|
|
171
|
-
- app.**onError**(err, handler)
|
|
172
|
-
- app.**fire**()
|
|
173
|
-
- app.**fetch**(request, env, event)
|
|
174
|
-
- app.**request**(path, options)
|
|
175
|
-
|
|
176
|
-
## Routing
|
|
177
|
-
|
|
178
|
-
### Basic
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
// HTTP Methods
|
|
182
|
-
app.get('/', (c) => c.text('GET /'))
|
|
183
|
-
app.post('/', (c) => c.text('POST /'))
|
|
184
|
-
app.put('/', (c) => c.text('PUT /'))
|
|
185
|
-
app.delete('/', (c) => c.text('DELETE /'))
|
|
186
|
-
|
|
187
|
-
// Wildcard
|
|
188
|
-
app.get('/wild/*/card', (c) => {
|
|
189
|
-
return c.text('GET /wild/*/card')
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// Any HTTP methods
|
|
193
|
-
app.all('/hello', (c) => c.text('Any Method /hello'))
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Named Parameter
|
|
197
|
-
|
|
198
|
-
```ts
|
|
199
|
-
app.get('/user/:name', (c) => {
|
|
200
|
-
const name = c.req.param('name')
|
|
201
|
-
...
|
|
202
|
-
})
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
or all parameters at once:
|
|
206
|
-
|
|
207
|
-
```ts
|
|
208
|
-
app.get('/posts/:id/comment/:comment_id', (c) => {
|
|
209
|
-
const { id, comment_id } = c.req.param()
|
|
210
|
-
...
|
|
211
|
-
})
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Regexp
|
|
215
|
-
|
|
216
|
-
```ts
|
|
217
|
-
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
|
|
218
|
-
const { date, title } = c.req.param()
|
|
219
|
-
...
|
|
220
|
-
})
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Chained route
|
|
224
|
-
|
|
225
|
-
```ts
|
|
226
|
-
app
|
|
227
|
-
.get('/endpoint', (c) => {
|
|
228
|
-
return c.text('GET /endpoint')
|
|
229
|
-
})
|
|
230
|
-
.post((c) => {
|
|
231
|
-
return c.text('POST /endpoint')
|
|
232
|
-
})
|
|
233
|
-
.delete((c) => {
|
|
234
|
-
return c.text('DELETE /endpoint')
|
|
235
|
-
})
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### no strict
|
|
239
|
-
|
|
240
|
-
If `strict` is set false, `/hello`and`/hello/` are treated the same.
|
|
241
|
-
|
|
242
|
-
```ts
|
|
243
|
-
const app = new Hono({ strict: false }) // Default is true
|
|
244
|
-
|
|
245
|
-
app.get('/hello', (c) => c.text('/hello or /hello/'))
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### async/await
|
|
249
|
-
|
|
250
|
-
```js
|
|
251
|
-
app.get('/fetch-url', async (c) => {
|
|
252
|
-
const response = await fetch('https://example.com/')
|
|
253
|
-
return c.text(`Status is ${response.status}`)
|
|
254
|
-
})
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
## Grouping
|
|
258
|
-
|
|
259
|
-
Group the routes with `Hono` instance and add them to the main app with `route` method.
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
const book = new Hono()
|
|
263
|
-
|
|
264
|
-
book.get('/', (c) => c.text('List Books')) // GET /book
|
|
265
|
-
book.get('/:id', (c) => {
|
|
266
|
-
// GET /book/:id
|
|
267
|
-
const id = c.req.param('id')
|
|
268
|
-
return c.text('Get Book: ' + id)
|
|
269
|
-
})
|
|
270
|
-
book.post('/', (c) => c.text('Create Book')) // POST /book
|
|
271
|
-
|
|
272
|
-
const app = new Hono()
|
|
273
|
-
app.route('/book', book)
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## Middleware
|
|
277
|
-
|
|
278
|
-
Middleware works after/before Handler. We can get `Request` before dispatching or manipulate `Response` after dispatching.
|
|
279
|
-
|
|
280
|
-
### Definition of Middleware
|
|
281
|
-
|
|
282
|
-
- Handler - should return `Response` object. Only one handler will be called.
|
|
283
|
-
- Middleware - should return nothing, will be proceeded to next middleware with `await next()`
|
|
284
|
-
|
|
285
|
-
The user can register middleware using `c.use` or using `c.HTTP_METHOD` as well as the handlers. For this feature, it's easy to specify the path and the method.
|
|
286
|
-
|
|
287
|
-
```ts
|
|
288
|
-
// match any method, all routes
|
|
289
|
-
app.use('*', logger())
|
|
290
|
-
|
|
291
|
-
// specify path
|
|
292
|
-
app.use('/posts/*', cors())
|
|
293
|
-
|
|
294
|
-
// specify method and path
|
|
295
|
-
app.post('/posts/*', basicAuth(), bodyParse())
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
If the handler returns `Response`, it will be used for the end-user, and stopping the processing.
|
|
299
|
-
|
|
300
|
-
```ts
|
|
301
|
-
app.post('/posts', (c) => c.text('Created!', 201))
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
In this case, four middleware are processed before dispatching like this:
|
|
305
|
-
|
|
306
|
-
```ts
|
|
307
|
-
logger() -> cors() -> basicAuth() -> bodyParse() -> *handler*
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Built-in Middleware
|
|
311
|
-
|
|
312
|
-
Hono has built-in middleware.
|
|
313
|
-
|
|
314
|
-
```ts
|
|
315
|
-
import { Hono } from 'hono'
|
|
316
|
-
import { poweredBy } from 'hono/powered-by'
|
|
317
|
-
import { logger } from 'hono/logger'
|
|
318
|
-
import { basicAuth } from 'hono/basicAuth'
|
|
319
|
-
|
|
320
|
-
const app = new Hono()
|
|
321
|
-
|
|
322
|
-
app.use('*', poweredBy())
|
|
323
|
-
app.use('*', logger())
|
|
324
|
-
|
|
325
|
-
app.use(
|
|
326
|
-
'/auth/*',
|
|
327
|
-
basicAuth({
|
|
328
|
-
username: 'hono',
|
|
329
|
-
password: 'acoolproject',
|
|
330
|
-
})
|
|
331
|
-
)
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
Available built-in middleware is listed on [src/middleware](https://github.com/honojs/hono/tree/master/src/middleware).
|
|
335
|
-
|
|
336
|
-
### Custom Middleware
|
|
337
|
-
|
|
338
|
-
You can write your own middleware.
|
|
339
|
-
|
|
340
|
-
```ts
|
|
341
|
-
// Custom logger
|
|
342
|
-
app.use('*', async (c, next) => {
|
|
343
|
-
console.log(`[${c.req.method}] ${c.req.url}`)
|
|
344
|
-
await next()
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
// Add a custom header
|
|
348
|
-
app.use('/message/*', async (c, next) => {
|
|
349
|
-
await next()
|
|
350
|
-
c.header('x-message', 'This is middleware!')
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
app.get('/message/hello', (c) => c.text('Hello Middleware!'))
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
## Not Found
|
|
357
|
-
|
|
358
|
-
`app.notFound` for customizing Not Found Response.
|
|
359
|
-
|
|
360
|
-
```js
|
|
361
|
-
app.notFound((c) => {
|
|
362
|
-
return c.text('Custom 404 Message', 404)
|
|
363
|
-
})
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
## Error Handling
|
|
367
|
-
|
|
368
|
-
`app.onError` handle the error and return the customized Response.
|
|
369
|
-
|
|
370
|
-
```js
|
|
371
|
-
app.onError((err, c) => {
|
|
372
|
-
console.error(`${err}`)
|
|
373
|
-
return c.text('Custom Error Message', 500)
|
|
374
|
-
})
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
## Context
|
|
378
|
-
|
|
379
|
-
To handle Request and Response, you can use `Context` object.
|
|
380
|
-
|
|
381
|
-
### c.req
|
|
382
|
-
|
|
383
|
-
```ts
|
|
384
|
-
// Get Request object
|
|
385
|
-
app.get('/hello', (c) => {
|
|
386
|
-
const userAgent = c.req.headers.get('User-Agent')
|
|
387
|
-
...
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
// Shortcut to get a header value
|
|
391
|
-
app.get('/shortcut', (c) => {
|
|
392
|
-
const userAgent = c.req.header('User-Agent')
|
|
393
|
-
...
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
// Query params
|
|
397
|
-
app.get('/search', (c) => {
|
|
398
|
-
const query = c.req.query('q')
|
|
399
|
-
...
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
// Get all params at once
|
|
403
|
-
app.get('/search', (c) => {
|
|
404
|
-
const { q, limit, offset } = c.req.query()
|
|
405
|
-
...
|
|
406
|
-
})
|
|
407
|
-
|
|
408
|
-
// Multiple query values
|
|
409
|
-
app.get('/search', (c) => {
|
|
410
|
-
const queries = c.req.queries('q')
|
|
411
|
-
// ---> GET search?q=foo&q=bar
|
|
412
|
-
// queries[0] => foo, queries[1] => bar
|
|
413
|
-
...
|
|
414
|
-
})
|
|
415
|
-
|
|
416
|
-
// Captured params
|
|
417
|
-
app.get('/entry/:id', (c) => {
|
|
418
|
-
const id = c.req.param('id')
|
|
419
|
-
...
|
|
420
|
-
})
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### Shortcuts for Response
|
|
424
|
-
|
|
425
|
-
```ts
|
|
426
|
-
app.get('/welcome', (c) => {
|
|
427
|
-
// Set headers
|
|
428
|
-
c.header('X-Message', 'Hello!')
|
|
429
|
-
c.header('Content-Type', 'text/plain')
|
|
430
|
-
|
|
431
|
-
// Set HTTP status code
|
|
432
|
-
c.status(201)
|
|
433
|
-
|
|
434
|
-
// Return the response body
|
|
435
|
-
return c.body('Thank you for comming')
|
|
436
|
-
})
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
The Response is the same as below.
|
|
440
|
-
|
|
441
|
-
```ts
|
|
442
|
-
new Response('Thank you for comming', {
|
|
443
|
-
status: 201,
|
|
444
|
-
headers: {
|
|
445
|
-
'X-Message': 'Hello',
|
|
446
|
-
'Content-Type': 'text/plain',
|
|
447
|
-
},
|
|
448
|
-
})
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
### c.text()
|
|
452
|
-
|
|
453
|
-
Render text as `Content-Type:text/plain`.
|
|
454
|
-
|
|
455
|
-
```ts
|
|
456
|
-
app.get('/say', (c) => {
|
|
457
|
-
return c.text('Hello!')
|
|
458
|
-
})
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
### c.json()
|
|
462
|
-
|
|
463
|
-
Render JSON as `Content-Type:application/json`.
|
|
464
|
-
|
|
465
|
-
```ts
|
|
466
|
-
app.get('/api', (c) => {
|
|
467
|
-
return c.json({ message: 'Hello!' })
|
|
468
|
-
})
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
### c.html()
|
|
472
|
-
|
|
473
|
-
Render HTML as `Content-Type:text/html`.
|
|
474
|
-
|
|
475
|
-
```ts
|
|
476
|
-
app.get('/', (c) => {
|
|
477
|
-
return c.html('<h1>Hello! Hono!</h1>')
|
|
478
|
-
})
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
### c.notFound()
|
|
482
|
-
|
|
483
|
-
Return the `Not Found` Response.
|
|
484
|
-
|
|
485
|
-
```ts
|
|
486
|
-
app.get('/notfound', (c) => {
|
|
487
|
-
return c.notFound()
|
|
488
|
-
})
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### c.redirect()
|
|
492
|
-
|
|
493
|
-
Redirect, default status code is `302`.
|
|
494
|
-
|
|
495
|
-
```ts
|
|
496
|
-
app.get('/redirect', (c) => c.redirect('/'))
|
|
497
|
-
app.get('/redirect-permanently', (c) => c.redirect('/', 301))
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
### c.res
|
|
501
|
-
|
|
502
|
-
```ts
|
|
503
|
-
// Response object
|
|
504
|
-
app.use('/', async (c, next) => {
|
|
505
|
-
await next()
|
|
506
|
-
c.res.headers.append('X-Debug', 'Debug message')
|
|
507
|
-
})
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
### c.executionCtx
|
|
511
|
-
|
|
512
|
-
```ts
|
|
513
|
-
// ExecutionContext object
|
|
514
|
-
app.get('/foo', async (c) => {
|
|
515
|
-
c.executionCtx.waitUntil(
|
|
516
|
-
c.env.KV.put(key, data)
|
|
517
|
-
)
|
|
518
|
-
...
|
|
519
|
-
})
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### c.event
|
|
523
|
-
|
|
524
|
-
```ts
|
|
525
|
-
// FetchEvent object (only set when using Service Worker syntax)
|
|
526
|
-
app.get('/foo', async (c) => {
|
|
527
|
-
c.event.waitUntil(
|
|
528
|
-
c.env.KV.put(key, data)
|
|
529
|
-
)
|
|
530
|
-
...
|
|
531
|
-
})
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
### c.env
|
|
535
|
-
|
|
536
|
-
Environment variables, secrets, and KV namespaces are known as bindings. Regardless of type, bindings are always available as global variables and can be accessed via the context `c.env.BINDING_KEY`.
|
|
537
|
-
|
|
538
|
-
```ts
|
|
539
|
-
// Environment object for Cloudflare Workers
|
|
540
|
-
app.get('*', async c => {
|
|
541
|
-
const counter = c.env.COUNTER
|
|
542
|
-
...
|
|
543
|
-
})
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
## fire
|
|
547
|
-
|
|
548
|
-
`app.fire()` do this.
|
|
549
|
-
|
|
550
|
-
```ts
|
|
551
|
-
addEventListener('fetch', (event) => {
|
|
552
|
-
event.respondWith(this.handleEvent(event))
|
|
553
|
-
})
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
## fetch
|
|
557
|
-
|
|
558
|
-
`app.fetch` for Cloudflare Module Worker syntax.
|
|
559
|
-
|
|
560
|
-
```ts
|
|
561
|
-
export default {
|
|
562
|
-
fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
563
|
-
return app.fetch(request, env, ctx)
|
|
564
|
-
},
|
|
565
|
-
}
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
or just do:
|
|
569
|
-
|
|
570
|
-
```ts
|
|
571
|
-
export default app
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
## request
|
|
575
|
-
|
|
576
|
-
`request` is a useful method for testing.
|
|
577
|
-
|
|
578
|
-
```js
|
|
579
|
-
test('GET /hello is ok', async () => {
|
|
580
|
-
const res = await app.request('http://localhost/hello')
|
|
581
|
-
expect(res.status).toBe(200)
|
|
582
|
-
})
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
## router
|
|
586
|
-
|
|
587
|
-
The `router` option specify which router is used inside. The default router is `TrieRouter`. If you want to use `RexExpRouter`, write like this:
|
|
588
|
-
|
|
589
|
-
```ts
|
|
590
|
-
import { RegExpRouter } from 'hono/router/reg-exp-router'
|
|
591
|
-
|
|
592
|
-
const app = new Hono({ router: new RegExpRouter() })
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
## Routing priority
|
|
596
|
-
|
|
597
|
-
Handlers or middleware will be executed in registration order.
|
|
598
|
-
|
|
599
|
-
```ts
|
|
600
|
-
app.get('/book/a', (c) => c.text('a')) // a
|
|
601
|
-
app.get('/book/:slug', (c) => c.text('common')) // common
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
```http
|
|
605
|
-
GET /book/a ---> `a`
|
|
606
|
-
GET /book/b ---> `common`
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
When a handler is executed, the process will be stopped.
|
|
610
|
-
|
|
611
|
-
```ts
|
|
612
|
-
app.get('*', (c) => c.text('common')) // common
|
|
613
|
-
app.get('/foo', (c) => c.text('foo')) // foo
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
```http
|
|
617
|
-
GET /foo ---> `common` // foo will not be dispatched
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
If you have the middleware that you want to execute, write the code above the handler.
|
|
621
|
-
|
|
622
|
-
```ts
|
|
623
|
-
app.use('*', logger())
|
|
624
|
-
app.get('/foo', (c) => c.text('foo'))
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
If you want a "_fallback_" handler, write the code below the other handler.
|
|
628
|
-
|
|
629
|
-
```ts
|
|
630
|
-
app.get('/foo', (c) => c.text('foo')) // foo
|
|
631
|
-
app.get('*', (c) => c.text('fallback')) // fallback
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
```http
|
|
635
|
-
GET /bar ---> `fallback`
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
## Cloudflare Workers with Hono
|
|
639
|
-
|
|
640
|
-
Using [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/), you can develop the application locally and publish it with few commands.
|
|
641
|
-
|
|
642
|
-
Let's write your first code for Cloudflare Workers with Hono.
|
|
643
|
-
|
|
644
|
-
### 1. `wrangler init`
|
|
645
|
-
|
|
646
|
-
Initialize as a wrangler project.
|
|
647
|
-
|
|
648
|
-
```
|
|
649
|
-
mkdir hono-example
|
|
650
|
-
cd hono-example
|
|
651
|
-
npx wrangler init -y
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
### 2. `npm install hono`
|
|
655
|
-
|
|
656
|
-
Install `hono` from the npm registry.
|
|
657
|
-
|
|
658
|
-
```
|
|
659
|
-
npm init -y
|
|
660
|
-
npm i hono
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
### 3. Write your app
|
|
664
|
-
|
|
665
|
-
Edit `src/index.ts`. Only 4 lines!!
|
|
666
|
-
|
|
667
|
-
```ts
|
|
668
|
-
// src/index.ts
|
|
669
|
-
import { Hono } from 'hono'
|
|
670
|
-
const app = new Hono()
|
|
671
|
-
|
|
672
|
-
app.get('/', (c) => c.text('Hello! Hono!'))
|
|
673
|
-
|
|
674
|
-
app.fire()
|
|
675
|
-
```
|
|
676
|
-
|
|
677
|
-
### 4. Run
|
|
678
|
-
|
|
679
|
-
Run the development server locally. Then, access `http://127.0.0.1:8787/` in your Web browser.
|
|
680
|
-
|
|
681
|
-
```
|
|
682
|
-
npx wrangler dev
|
|
683
|
-
```
|
|
684
|
-
|
|
685
|
-
### 5. Publish
|
|
686
|
-
|
|
687
|
-
Deploy to Cloudflare. That's all!
|
|
688
|
-
|
|
689
|
-
```
|
|
690
|
-
npx wrangler publish ./src/index.ts
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
## Starter template
|
|
694
|
-
|
|
695
|
-
You can start making your Cloudflare Workers application with [the starter template](https://github.com/honojs/hono-minimal). It is really minimal using TypeScript, esbuild, Miniflare, and Jest.
|
|
696
|
-
|
|
697
|
-
To generate a project skeleton, run this command.
|
|
698
|
-
|
|
699
|
-
```
|
|
700
|
-
npx create-cloudflare my-app https://github.com/honojs/hono-minimal
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
## Practical Example
|
|
704
|
-
|
|
705
|
-
How about writing web API with Hono?
|
|
706
|
-
|
|
707
|
-
```ts
|
|
708
|
-
import { Hono } from 'hono'
|
|
709
|
-
import { cors } from 'hono/cors'
|
|
710
|
-
import { basicAuth } from 'hono/basic-auth'
|
|
711
|
-
import { prettyJSON } from 'hono/pretty-json'
|
|
712
|
-
import { getPosts, getPost, createPost, Post } from './model'
|
|
713
|
-
|
|
714
|
-
const app = new Hono()
|
|
715
|
-
app.get('/', (c) => c.text('Pretty Blog API'))
|
|
716
|
-
app.use('*', prettyJSON())
|
|
717
|
-
app.notFound((c) => c.json({ message: 'Not Found', ok: false }, 404))
|
|
718
|
-
|
|
719
|
-
export interface Bindings {
|
|
720
|
-
USERNAME: string
|
|
721
|
-
PASSWORD: string
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
const api = new Hono<Bindings>()
|
|
725
|
-
api.use('/posts/*', cors())
|
|
726
|
-
|
|
727
|
-
api.get('/posts', (c) => {
|
|
728
|
-
const { limit, offset } = c.req.query()
|
|
729
|
-
const posts = getPosts({ limit, offset })
|
|
730
|
-
return c.json({ posts })
|
|
731
|
-
})
|
|
732
|
-
|
|
733
|
-
api.get('/posts/:id', (c) => {
|
|
734
|
-
const id = c.req.param('id')
|
|
735
|
-
const post = getPost({ id })
|
|
736
|
-
return c.json({ post })
|
|
737
|
-
})
|
|
738
|
-
|
|
739
|
-
api.post(
|
|
740
|
-
'/posts',
|
|
741
|
-
async (c, next) => {
|
|
742
|
-
const auth = basicAuth({ username: c.env.USERNAME, password: c.env.PASSWORD })
|
|
743
|
-
await auth(c, next)
|
|
744
|
-
},
|
|
745
|
-
async (c) => {
|
|
746
|
-
const post = await c.req.json<Post>()
|
|
747
|
-
const ok = createPost({ post })
|
|
748
|
-
return c.json({ ok })
|
|
749
|
-
}
|
|
750
|
-
)
|
|
751
|
-
|
|
752
|
-
app.route('/api', api)
|
|
753
|
-
|
|
754
|
-
export default app
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
## Other Examples
|
|
758
|
-
|
|
759
|
-
- Hono Examples - <https://github.com/honojs/examples>
|
|
760
|
-
|
|
761
|
-
## Deno
|
|
762
|
-
|
|
763
|
-
Hono also works on Deno. This feature is still experimental.
|
|
764
|
-
|
|
765
|
-
```tsx
|
|
766
|
-
/** @jsx jsx */
|
|
767
|
-
import { serve } from 'https://deno.land/std/http/server.ts'
|
|
768
|
-
import { Hono, logger, poweredBy, serveStatic, jsx } from 'https://deno.land/x/hono/mod.ts'
|
|
769
|
-
|
|
770
|
-
const app = new Hono()
|
|
771
|
-
|
|
772
|
-
app.use('*', logger(), poweredBy())
|
|
773
|
-
|
|
774
|
-
app.get('/favicon.ico', serveStatic({ path: './public/favicon.ico' }))
|
|
775
|
-
app.get('/', (c) => {
|
|
776
|
-
return c.html(<h1>Hello Deno!</h1>)
|
|
777
|
-
})
|
|
778
|
-
|
|
779
|
-
serve(app.fetch)
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
## Bun
|
|
783
|
-
|
|
784
|
-
Hono also works on Bun. This feature is still experimental.
|
|
785
|
-
|
|
786
|
-
```ts
|
|
787
|
-
import { Hono } from 'hono'
|
|
788
|
-
|
|
789
|
-
const app = new Hono()
|
|
790
|
-
|
|
791
|
-
app.get('/', (c) => {
|
|
792
|
-
return c.json({ message: 'Hello Bun!' })
|
|
793
|
-
})
|
|
794
|
-
|
|
795
|
-
export default {
|
|
796
|
-
port: 3000,
|
|
797
|
-
fetch: app.fetch,
|
|
798
|
-
}
|
|
799
|
-
```
|
|
58
|
+
## Documentation
|
|
800
59
|
|
|
801
|
-
|
|
60
|
+
The documentation is available on [honojs.dev](https://honojs.dev).
|
|
802
61
|
|
|
803
|
-
|
|
62
|
+
## Migration
|
|
804
63
|
|
|
805
|
-
|
|
806
|
-
- koa - <https://github.com/koajs/koa>
|
|
807
|
-
- itty-router - <https://github.com/kwhitley/itty-router>
|
|
808
|
-
- Sunder - <https://github.com/SunderJS/sunder>
|
|
809
|
-
- goblin - <https://github.com/bmf-san/goblin>
|
|
810
|
-
- worktop - <https://github.com/lukeed/worktop>
|
|
811
|
-
- Router::Boom - <https://github.com/tokuhirom/Router-Boom>
|
|
64
|
+
Migration guide is available on [docs/MIGRATION.md](docs/MIGRATION.md)
|
|
812
65
|
|
|
813
66
|
## Contributing
|
|
814
67
|
|
|
815
68
|
Contributions Welcome! You can contribute in the following ways.
|
|
816
69
|
|
|
817
|
-
-
|
|
818
|
-
-
|
|
819
|
-
-
|
|
820
|
-
- Refactor the code
|
|
70
|
+
- Fix bugs.
|
|
71
|
+
- Create built-in or third-party middleware.
|
|
72
|
+
- Propose new feature.
|
|
73
|
+
- Refactor the code.
|
|
74
|
+
- Write an article about Hono on your Blog.
|
|
75
|
+
- Fix a typo.
|
|
821
76
|
- etc.
|
|
822
77
|
|
|
823
78
|
## Contributors
|