HTTP Method Handlers
Hono provides method-specific handlers for defining routes. Each handler corresponds to an HTTP method:
const app = new Hono()
app.get('/hello', (c) => c.text('GET request'))
app.post('/hello', (c) => c.text('POST request'))
app.put('/hello', (c) => c.text('PUT request'))
app.delete('/hello', (c) => c.text('DELETE request'))
app.patch('/hello', (c) => c.text('PATCH request'))
app.options('/hello', (c) => c.text('OPTIONS request'))
The available method handlers are defined in src/router.ts:17:
export const METHODS = ['get', 'post', 'put', 'delete', 'options', 'patch'] as const
Method Chaining
You can chain multiple handlers on the same path:
app.post('/books').get((c) => {
return c.text('Get books')
})
// This creates a GET handler for /books
Handlers Without a Path
You can define handlers without specifying a path. The path from the previous call will be used:
const app = new Hono()
app.get((c) => {
return c.text('Hello from root')
})
// This handles GET requests to '/'
All Methods Handler
The all() method matches any HTTP method:
app.all('/api/*', (c) => {
return c.text('Matches all methods')
})
Custom Methods with on()
For custom HTTP methods or multiple methods at once, use on():
// Single method
app.on('PURGE', '/cache', (c) => {
return c.text('Cache purged')
})
// Multiple methods
app.on(['GET', 'POST'], '/data', (c) => {
return c.text(`${c.req.method} request`)
})
// Multiple paths
app.on('GET', ['/hello', '/hi'], (c) => {
return c.text('Hello')
})
The implementation in src/hono-base.ts:144 shows how on() works:
this.on = (method: string | string[], path: string | string[], ...handlers: H[]) => {
for (const p of [path].flat()) {
this.#path = p
for (const m of [method].flat()) {
handlers.map((handler) => {
this.#addRoute(m.toUpperCase(), this.#path, handler)
})
}
}
return this as any
}
Route Order and Matching
Routes are matched in the order they are defined. However, the specific router implementation determines the exact matching behavior:
The default router uses a smart matching algorithm that prioritizes more specific routes over wildcards.
app.get('/posts/featured', (c) => c.text('Featured posts'))
app.get('/posts/:id', (c) => c.text(`Post ${c.req.param('id')}`))
// GET /posts/featured -> "Featured posts"
// GET /posts/123 -> "Post 123"
Wildcard Routes
Use * to match any path segment:
// Match any path under /api
app.get('/api/*', (c) => {
return c.text('API endpoint')
})
// Match everything
app.get('*', (c) => {
return c.text('Catch all')
})
From src/router/reg-exp-router/router.ts:149, wildcards are handled specially:
if (path === '/*') {
path = '*'
}
Pattern Matching
Hono supports various path patterns:
// Static paths
app.get('/about', (c) => c.text('About'))
// With parameters
app.get('/users/:id', (c) => c.text(`User ${c.req.param('id')}`))
// Optional segments (see next page for details)
app.get('/posts/:id/comments?', (c) => c.text('Post with optional comments'))
// Regex patterns
app.get('/date/:date{[0-9]+}', (c) => {
return c.text(`Date: ${c.req.param('date')}`)
})
Multiple Handlers
You can register multiple handlers for the same route by calling the method multiple times:
// First handler (middleware-like)
app.get('/hello', async (c, next) => {
console.log('Before handler')
await next()
})
// Second handler (final response)
app.get('/hello', (c) => {
return c.text('Hello!')
})
Alternatively, pass multiple handlers in one call:
app.get('/hello',
async (c, next) => {
console.log('Middleware')
await next()
},
(c) => c.text('Hello!')
)
The implementation in src/hono-base.ts:130 shows this pattern:
this[method] = (args1: string | H, ...args: H[]) => {
if (typeof args1 === 'string') {
this.#path = args1
} else {
this.#addRoute(method, this.#path, args1)
}
args.forEach((handler) => {
this.#addRoute(method, this.#path, handler)
})
return this as any
}
Strict Mode
By default, Hono uses strict mode which distinguishes between paths with and without trailing slashes:
// With strict mode (default)
const app = new Hono({ strict: true })
app.get('/hello', (c) => c.text('Hello'))
// GET /hello -> 200
// GET /hello/ -> 404
// Without strict mode
const app2 = new Hono({ strict: false })
app2.get('/hello', (c) => c.text('Hello'))
// GET /hello -> 200
// GET /hello/ -> 200
From src/hono-base.ts:172, the strict option affects the path matching:
this.getPath = (strict ?? true) ? (options.getPath ?? getPath) : getPathNoStrict