Overview
Introduction
Routing and handlers form the core of any application built with the library. This guide explains how to register HTTP routes, extract path parameters and query strings, chain multiple route handlers or middleware together, and inspect matching route metadata.
Registering HTTP Routes
You can define routes using standard HTTP method helper functions on your application instance, or use the .on() method to specify custom or multiple methods at once.
To register routes:
- Import and instantiate the application class.
- Call a method helper like
app.get(),app.post(), orapp.on()passing a path string and one or more handler functions.
import { Hono } from 'hono'
const app = new Hono()
// Basic GET route
app.get('/hello', (c) => c.text('Hello!'))
// Registering multiple HTTP methods to a single path using .on()
app.on(['PUT', 'DELETE'], '/posts/:id', (c) => {
const id = c.req.param('id')
return c.text(`Handled PUT or DELETE for post: ${id}`)
})Supported HTTP method helper names are: get, post, put, delete, options, patch, and all.
Extracting Parameters and Query Strings
Path and Query Parameters
Route paths can contain named dynamic segments and optional parameters. You can retrieve these parameters alongside URL query string values using the request object (c.req) available in the context.
Path parameters are prefixed with a colon (:). You can also specify custom regular expression patterns for parameters using braces (e.g., :id{[0-9]+}). Optional parameters can be designated with a trailing question mark (?).
app.get('/users/:id', (c) => {
// Get a single path parameter
const id = c.req.param('id')
// Or retrieve all path parameters as an object
// const { id } = c.req.param()
return c.text(`User ID: ${id}`)
})Query parameters can be extracted individually or in aggregate using .query() and .queries() (for multi-value parameters like ?tags=js&tags=web).
app.get('/search', (c) => {
// Get a single query parameter value (?q=term)
const query = c.req.query('q')
// Get multiple query parameter values as an array (?tags=a&tags=b)
const tags = c.req.queries('tags')
return c.json({ query, tags })
})Chaining Route Handlers and Middleware
You can pass multiple handler functions to a single route registration. Handlers execute sequentially as a chain. Every handler except the final response generator must call await next() to pass control to the subsequent handler in the chain.
import { Hono } from 'hono'
const app = new Hono()
const authMiddleware = async (c, next) => {
const token = c.req.header('Authorization')
if (!token) {
return c.text('Unauthorized', 401)
}
await next()
}
// Chain middleware and the final route handler
app.get('/dashboard', authMiddleware, (c) => {
return c.text('Welcome to the protected dashboard!')
})Tip
If a handler does not call await next() and does not return a Response, the execution chain will short-circuit. Ensure you always return a Response or call await next().
Inspecting Matched Routes
You can inspect metadata about the currently matched routes during request handling by using helper utilities imported from hono/route.
import { Hono } from 'hono'
import { routePath, matchedRoutes } from 'hono/route'
const app = new Hono()
app.use('/posts/*', async (c, next) => {
console.log('Current route path pattern:', routePath(c))
await next()
})
app.get('/posts/:id', (c) => {
return c.text(`Viewing post ${c.req.param('id')}`)
})Router Engines
The library uses a smart routing strategy by default that automatically selects the highest-performing underlying router engine depending on your route patterns.
Supported built-in router implementations include:
SmartRouter(default wrapper that selects between available routers)LinearRouterTrieRouterRegExpRouterPreparedRegExpRouter
Note
You typically do not need to configure router engines manually unless you require specific pre-compiled RegExp serialization optimization for serverless startup performance.