Documentation Index
Fetch the complete documentation index at: https://mintlify.com/honojs/hono/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Routing in Hono maps HTTP requests to handler functions based on the request method and URL path. Hono provides a simple and intuitive API for defining routes with full type safety.
Defining Routes
Routes are defined using HTTP method helpers on the Hono application instance. Each method corresponds to a standard HTTP verb.
Basic Route Definition
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
app.post('/posts', (c) => {
return c.json({ message: 'Post created' })
})
HTTP Method Handlers
Hono supports all standard HTTP methods through dedicated handler functions:
app.get('/users', (c) => c.text('GET /users'))
app.post('/users', (c) => c.text('POST /users'))
app.put('/users/:id', (c) => c.text('PUT /users/:id'))
app.delete('/users/:id', (c) => c.text('DELETE /users/:id'))
app.patch('/users/:id', (c) => c.text('PATCH /users/:id'))
app.options('/users', (c) => c.text('OPTIONS /users'))
Available Methods
From src/hono-base.ts:104-110:
get!: HandlerInterface<E, 'get', S, BasePath, CurrentPath>
post!: HandlerInterface<E, 'post', S, BasePath, CurrentPath>
put!: HandlerInterface<E, 'put', S, BasePath, CurrentPath>
delete!: HandlerInterface<E, 'delete', S, BasePath, CurrentPath>
options!: HandlerInterface<E, 'options', S, BasePath, CurrentPath>
patch!: HandlerInterface<E, 'patch', S, BasePath, CurrentPath>
all!: HandlerInterface<E, 'all', S, BasePath, CurrentPath>
The all Method
The all method matches any HTTP method:
app.all('/api/*', (c) => {
return c.text('Matches all methods')
})
Path Patterns
Hono supports various path patterns for flexible route matching.
Static Paths
Exact path matching:
app.get('/about', (c) => c.text('About page'))
app.get('/contact', (c) => c.text('Contact page'))
Path Parameters
Capture dynamic segments using :param syntax:
app.get('/users/:id', (c) => {
const id = c.req.param('id')
return c.json({ userId: id })
})
app.get('/posts/:postId/comments/:commentId', (c) => {
const { postId, commentId } = c.req.param()
return c.json({ postId, commentId })
})
Wildcard Paths
Match multiple path segments:
app.get('/files/*', (c) => {
return c.text('Matches /files/any/path/here')
})
The on Method
For custom methods or matching multiple methods, use the on method:
// Single method
app.on('CUSTOM', '/webhook', (c) => {
return c.text('Custom method handler')
})
// Multiple methods
app.on(['GET', 'POST'], '/dual', (c) => {
return c.text(`Handled ${c.req.method}`)
})
// Multiple paths
app.on('GET', ['/path1', '/path2'], (c) => {
return c.text('Multiple paths')
})
From src/hono-base.ts:144-154:
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 Grouping
Group routes with a common base path using route:
const api = new Hono()
api.get('/users', (c) => c.json({ users: [] }))
api.get('/posts', (c) => c.json({ posts: [] }))
const app = new Hono()
app.route('/api', api)
// Now accessible at /api/users and /api/posts
From src/hono-base.ts:208-232:
route<
SubPath extends string,
SubEnv extends Env,
SubSchema extends Schema,
SubBasePath extends string,
SubCurrentPath extends string,
>(
path: SubPath,
app: Hono<SubEnv, SubSchema, SubBasePath, SubCurrentPath>
): Hono<E, MergeSchemaPath<SubSchema, MergePath<BasePath, SubPath>> | S, BasePath, CurrentPath>
Base Paths
Define a base path for all routes in an instance:
const api = new Hono().basePath('/api/v1')
api.get('/users', (c) => c.text('GET /api/v1/users'))
api.get('/posts', (c) => c.text('GET /api/v1/posts'))
From src/hono-base.ts:247-253:
basePath<SubPath extends string>(
path: SubPath
): Hono<E, S, MergePath<BasePath, SubPath>, MergePath<BasePath, SubPath>> {
const subApp = this.#clone()
subApp._basePath = mergePath(this._basePath, path)
return subApp
}
Route Matching
Routes are matched in the order they are defined. The first matching route handles the request.
Strict Mode
By default, Hono uses strict mode which distinguishes /path from /path/:
const app = new Hono({ strict: true }) // default
app.get('/users', handler) // Matches /users only
app.get('/posts/', handler) // Matches /posts/ only
Disable strict mode to treat both the same:
const app = new Hono({ strict: false })
app.get('/users', handler) // Matches both /users and /users/
From src/hono-base.ts:46-87:
type HonoOptions<E extends Env> = {
/**
* `strict` option specifies whether to distinguish whether the last path is a directory or not.
*
* @default true
*/
strict?: boolean
/**
* `router` option specifies which router to use.
*/
router?: Router<[H, RouterRoute]>
/**
* `getPath` can handle the host header value.
*/
getPath?: GetPath<E>
}
Chaining Routes
Multiple handlers can be chained on the same path:
app
.get('/users', (c) => c.json({ message: 'Getting users' }))
.post('/users', (c) => c.json({ message: 'Creating user' }))
.put('/users/:id', (c) => c.json({ message: 'Updating user' }))
From src/hono-base.ts:127-141, routes return the app instance for chaining:
allMethods.forEach((method) => {
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
}
})
Access registered routes through the routes property:
const app = new Hono()
app.get('/users', handler)
app.post('/posts', handler)
console.log(app.routes)
// Returns RouterRoute[] with route metadata
From src/types.ts:57-62:
interface RouterRoute {
basePath: string
path: string
method: string
handler: H
}
Best Practices
- Define specific routes before wildcard routes
- Use route grouping for better organization
- Leverage TypeScript for path parameter type safety
- Use
basePath for API versioning
Wildcard routes (*) should be defined last, as they will match any path that hasn’t been matched by previous routes.