Hono[炎] - means flame🔥 in Japanese - is small, simple, and ultrafast web flamework for a Service Workers API based serverless such as Cloudflare Workers and Fastly Compute@Edge.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!!'))
app.fire()- Ultra fast - the router is implemented with Trie-Tree structure.
- Zero dependencies - using only Web standard API.
- Middleware - builtin middleware, and you can make your own middleware.
- Optimized - for Cloudflare Workers.
Hono is fastest compared to other routers for Cloudflare Workers.
hono x 748,188 ops/sec ±5.40% (77 runs sampled)
itty-router x 158,817 ops/sec ±3.62% (87 runs sampled)
sunder x 332,339 ops/sec ±1.11% (95 runs sampled)
worktop x 205,906 ops/sec ±4.43% (83 runs sampled)
Fastest is hono
✨ Done in 52.79s.
Below is a demonstration to create an application of Cloudflare Workers with Hono.
You can install from npm registry:
yarn add honoor
npm install honoInstance of Hono has these methods:
- app.HTTP_METHOD(path, handler)
- app.all(path, handler)
- app.route(path)
- app.use(path, middleware)
- app.fire()
- app.fetch(request, env, event)
app.HTTP_METHOD
// HTTP Methods
app.get('/', (c) => c.text('GET /'))
app.post('/', (c) => c.text('POST /'))
// Wildcard
app.get('/wild/*/card', (c) => {
return c.text('GET /wild/*/card')
})app.all
// Any HTTP methods
app.all('/hello', (c) => c.text('Any Method /hello'))app.get('/user/:name', (c) => {
const name = c.req.params('name')
...
})app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
const date = c.req.params('date')
const title = c.req.params('title')
...app
.route('/api/book')
.get(() => {...})
.post(() => {...})
.put(() => {...})app.get('/fetch-url', async (c) => {
const response = await fetch('https://example.com/')
return c.text(`Status is ${response.status}`)
})import { Hono, Middleware } from 'hono'
...
app.use('*', Middleware.poweredBy())
app.use('*', Middleware.logger())
app.use(
'/auth/*',
Middleware.basicAuth({
username: 'hono',
password: 'acoolproject',
})
)Available builtin middleware are listed on src/middleware.
You can write your own middleware:
// Custom logger
app.use('*', async (c, next) => {
console.log(`[${c.req.method}] ${c.req.url}`)
await next()
})
// Add a custom header
app.use('/message/*', async (c, next) => {
await next()
await c.res.headers.add('x-message', 'This is middleware!')
})
app.get('/message/hello', (c) => c.text('Hello Middleware!'))You can customize 404 Not Found response:
app.use('*', async (c, next) => {
await next()
if (c.res.status === 404) {
c.res = new Response('Custom 404 Not Found', { status: 404 })
}
})app.use('*', async (c, next) => {
try {
await next()
} catch (err) {
console.error(`${err}`)
c.res = new Response('Custom Error Message', { status: 500 })
}
})You can also do this:
// Output response time
app.use('*', async (c, next) => {
await next()
const responseTime = await c.res.headers.get('X-Response-Time')
console.log(`X-Response-Time: ${responseTime}`)
})
// Add X-Response-Time header
app.use('*', async (c, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
await c.res.headers.append('X-Response-Time', `${ms}ms`)
})To handle Request and Reponse easily, you can use Context object:
// Get Request object
app.get('/hello', (c) => {
const userAgent = c.req.headers.get('User-Agent')
...
})
// Query params
app.get('/search', (c) => {
const query = c.req.query('q')
...
})
// Captured params
app.get('/entry/:id', (c) => {
const id = c.req.params('id')
...
})// Response object
app.use('/', (c, next) => {
next()
c.res.headers.append('X-Debug', 'Debug message')
})// FetchEvent object
app.use('*', async (c, next) => {
c.event.waitUntil(
...
)
await next()
})// Environment object for Cloudflare Workers
app.get('*', async c => {
const counter = c.env.COUNTER
...
})Render text as Content-Type:text/plain:
app.get('/say', (c) => {
return c.text('Hello!')
})Render JSON as Content-Type:application/json:
app.get('/api', (c) => {
return c.json({ message: 'Hello!' })
})Render HTML as Content-Type:text/html:
app.get('/', (c) => {
return c.html('<h1>Hello! Hono!</h1>')
})Redirect, default status code is 302:
app.get('/redirect', (c) => c.redirect('/'))
app.get('/redirect-permanently', (c) => c.redirect('/', 301))app.fire() do:
addEventListener('fetch', (event) => {
event.respondWith(this.handleEvent(event))
})app.fetch() is for Cloudflare Module Worker syntax.
export default {
fetch(request: Request, env: Env, event: FetchEvent) {
return app.fetch(request, env, event)
},
}Using wrangler or miniflare, you can develop the application locally and publish it with few commands.
Let's write your first code for Cloudflare Workers with Hono.
Install Cloudflare Command Line "Wrangler"
npm i @cloudflare/wrangler -gMake npm skeleton directory.
mkdir hono-example
cd hono-example
npm init -yInit as a wrangler project.
wrangler initInstall hono from npm registry.
npm i honoOnly 4 lines!!
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello! Hono!'))
app.fire()Run the development server locally. Then, access like http://127.0.0.1:8787/ in your Web browser.
wrangler devDeploy to Cloudflare. That's all!
wrangler publishImplementation of the router is inspired by goblin. API design is inspired by express and koa. itty-router, Sunder, and worktop are the other routers or frameworks for Cloudflare Workers.
- express https://github.com/expressjs/express
- koa https://github.com/koajs/koa
- itty-router https://github.com/kwhitley/itty-router
- Sunder https://github.com/SunderJS/sunder
- goblin https://github.com/bmf-san/goblin
- worktop https://github.com/lukeed/worktop
Contributions Welcome! You can contribute by the following way:
- Write or fix documents
- Write code of middleware
- Fix bugs
- Refactor the code
- etc.
If you can, let's make Hono together!
Yusuke Wada https://github.com/yusukebe
Distributed under the MIT License. See LICENSE for more information.