Routing
Routing of Hono is flexible and intuitive. Let's take a look.
Basic
// HTTP Methods
app.get('/', (c) => c.text('GET /'))
app.post('/', (c) => c.text('POST /'))
app.put('/', (c) => c.text('PUT /'))
app.delete('/', (c) => c.text('DELETE /'))
// Wildcard
app.get('/wild/*/card', (c) => {
return c.text('GET /wild/*/card')
})
// Any HTTP methods
app.all('/hello', (c) => c.text('Any Method /hello'))
// Custom HTTP method
app.on('PURGE', '/cache', (c) => c.text('PURGE Method /cache'))
// Multiple Method
app.on(['PUT', 'DELETE'], '/post', (c) => c.text('PUT or DELETE /post'))// HTTP Methods
app.get('/', (c) => c.text('GET /'))
app.post('/', (c) => c.text('POST /'))
app.put('/', (c) => c.text('PUT /'))
app.delete('/', (c) => c.text('DELETE /'))
// Wildcard
app.get('/wild/*/card', (c) => {
return c.text('GET /wild/*/card')
})
// Any HTTP methods
app.all('/hello', (c) => c.text('Any Method /hello'))
// Custom HTTP method
app.on('PURGE', '/cache', (c) => c.text('PURGE Method /cache'))
// Multiple Method
app.on(['PUT', 'DELETE'], '/post', (c) => c.text('PUT or DELETE /post'))Path Parameter
app.get('/user/:name', (c) => {
const name = c.req.param('name')
...
})app.get('/user/:name', (c) => {
const name = c.req.param('name')
...
})or all parameters at once:
app.get('/posts/:id/comment/:comment_id', (c) => {
const { id, comment_id } = c.req.param()
...
})app.get('/posts/:id/comment/:comment_id', (c) => {
const { id, comment_id } = c.req.param()
...
})Optional Parameter
// Will match `/api/animal` and `/api/animal/:type`
app.get('/api/animal/:type?', (c) => c.text('Animal!'))// Will match `/api/animal` and `/api/animal/:type`
app.get('/api/animal/:type?', (c) => c.text('Animal!'))Regexp
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
const { date, title } = c.req.param()
...
})app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
const { date, title } = c.req.param()
...
})Including slashes
app.get('/posts/:filename{.+\\.png$}', (c) => {
//...
})app.get('/posts/:filename{.+\\.png$}', (c) => {
//...
})Chained route
app
.get('/endpoint', (c) => {
return c.text('GET /endpoint')
})
.post((c) => {
return c.text('POST /endpoint')
})
.delete((c) => {
return c.text('DELETE /endpoint')
})app
.get('/endpoint', (c) => {
return c.text('GET /endpoint')
})
.post((c) => {
return c.text('POST /endpoint')
})
.delete((c) => {
return c.text('DELETE /endpoint')
})Grouping
You can group the routes with the Hono instance and add them to the main app with the route method.
const book = new Hono()
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
const app = new Hono()
app.route('/book', book)const book = new Hono()
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
const app = new Hono()
app.route('/book', book)Base path
You can specify the base path.
const api = new Hono().basePath('/api')
api.get('/book', (c) => c.text('List Books')) // GET /api/bookconst api = new Hono().basePath('/api')
api.get('/book', (c) => c.text('List Books')) // GET /api/bookRouting with hostname
It works fine if it includes a hostname.
const app = new Hono({
getPath: (req) => req.url.replace(/^https?:\/(.+?)$/, '$1'),
})
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
app.get('/www2.example.com/hello', (c) => c.text('hello www2'))const app = new Hono({
getPath: (req) => req.url.replace(/^https?:\/(.+?)$/, '$1'),
})
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
app.get('/www2.example.com/hello', (c) => c.text('hello www2'))Routing with host Header value
Hono can handle the host header value if you set the getPath() function in the Hono constructor.
const app = new Hono({
getPath: (req) =>
'/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
})
app.get('/www1.example.com/hello', () => c.text('hello www1'))
// A following request will match the route:
// new Request('http://www1.example.com/hello', {
// headers: { host: 'www1.example.com' },
// })const app = new Hono({
getPath: (req) =>
'/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
})
app.get('/www1.example.com/hello', () => c.text('hello www1'))
// A following request will match the route:
// new Request('http://www1.example.com/hello', {
// headers: { host: 'www1.example.com' },
// })By applying this, for example, you can change the routing by User-Agent header.
Routing priority
Handlers or middleware will be executed in registration order.
app.get('/book/a', (c) => c.text('a')) // a
app.get('/book/:slug', (c) => c.text('common')) // commonapp.get('/book/a', (c) => c.text('a')) // a
app.get('/book/:slug', (c) => c.text('common')) // commonGET /book/a ---> `a`
GET /book/b ---> `common`GET /book/a ---> `a`
GET /book/b ---> `common`When a handler is executed, the process will be stopped.
app.get('*', (c) => c.text('common')) // common
app.get('/foo', (c) => c.text('foo')) // fooapp.get('*', (c) => c.text('common')) // common
app.get('/foo', (c) => c.text('foo')) // fooGET /foo ---> `common` // foo will not be dispatchedGET /foo ---> `common` // foo will not be dispatchedIf you have the middleware that you want to execute, write the code above the handler.
app.use('*', logger())
app.get('/foo', (c) => c.text('foo'))app.use('*', logger())
app.get('/foo', (c) => c.text('foo'))If you want to have a "fallback" handler, write the code below the other handler.
app.get('/foo', (c) => c.text('foo')) // foo
app.get('*', (c) => c.text('fallback')) // fallbackapp.get('/foo', (c) => c.text('foo')) // foo
app.get('*', (c) => c.text('fallback')) // fallbackGET /bar ---> `fallback`GET /bar ---> `fallback`Grouping ordering
Note that the mistake of grouping routings is hard to notice. 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.
three.get('/hi', (c) => c.text('hi'))
two.route('/three', three)
app.route('/two', two)
export default appthree.get('/hi', (c) => c.text('hi'))
two.route('/three', three)
app.route('/two', two)
export default appIt will return 200 response.
GET /two/three/hi ---> `hi`GET /two/three/hi ---> `hi`However, if they are in the wrong order, it will return a 404.
three.get('/hi', (c) => c.text('hi'))
app.route('/two', two) // `two` does not have routes
two.route('/three', three)
export default appthree.get('/hi', (c) => c.text('hi'))
app.route('/two', two) // `two` does not have routes
two.route('/three', three)
export default appGET /two/three/hi ---> 404 Not FoundGET /two/three/hi ---> 404 Not Found
Hono