Skip to main content

Routing

Hono provides a flexible and powerful routing system that supports various path patterns, parameters, and multiple router implementations.

Route Patterns

Basic Routes

Define routes using simple string paths.
app.get('/', (c) => c.text('Home'))
app.get('/about', (c) => c.text('About'))
app.get('/contact', (c) => c.text('Contact'))

Path Parameters

Capture dynamic segments in the URL path.
// Single parameter
app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ userId: id })
})

// Multiple parameters
app.get('/posts/:postId/comments/:commentId', (c) => {
  const { postId, commentId } = c.req.param()
  return c.json({ postId, commentId })
})

// Optional parameters
app.get('/users/:id?', (c) => {
  const id = c.req.param('id') // string | undefined
  return c.json({ id })
})

Wildcard Routes

Match multiple path segments.
// Match any path
app.get('*', (c) => c.text('Catch all'))

// Match paths starting with /api/
app.get('/api/*', (c) => c.text('API route'))

// Match with prefix
app.get('/files/*', (c) => {
  const path = c.req.path // e.g., '/files/documents/report.pdf'
  return c.text(`File: ${path}`)
})

Regular Expressions

Use regex patterns in routes.
// Match numeric IDs only
app.get('/users/:id{[0-9]+}', (c) => {
  const id = c.req.param('id') // guaranteed to be numeric string
  return c.json({ userId: id })
})

// Match specific patterns
app.get('/posts/:slug{[a-z-]+}', (c) => {
  const slug = c.req.param('slug')
  return c.json({ slug })
})

Multiple Paths

Register the same handler for multiple paths using app.on().
app.on('GET', ['/v1/users', '/v2/users'], (c) => {
  return c.json({ users: [] })
})

Route Methods

HTTP Methods

Hono supports all standard HTTP methods.
app.get('/resource', (c) => c.text('GET'))
app.post('/resource', (c) => c.text('POST'))
app.put('/resource', (c) => c.text('PUT'))
app.patch('/resource', (c) => c.text('PATCH'))
app.delete('/resource', (c) => c.text('DELETE'))
app.options('/resource', (c) => c.text('OPTIONS'))

All Methods

Match all HTTP methods for a route.
app.all('/api/status', (c) => {
  return c.json({ status: 'ok', method: c.req.method })
})

Custom Methods

Handle custom HTTP methods.
app.on('PURGE', '/cache', (c) => {
  return c.text('Cache purged')
})

app.on(['LINK', 'UNLINK'], '/resource', (c) => {
  return c.text(`Method: ${c.req.method}`)
})

Route Grouping

Using basePath

Create a Hono instance with a base path prefix.
const api = new Hono().basePath('/api/v1')

api.get('/users', (c) => c.json({ users: [] }))       // GET /api/v1/users
api.get('/posts', (c) => c.json({ posts: [] }))       // GET /api/v1/posts
api.get('/comments', (c) => c.json({ comments: [] })) // GET /api/v1/comments

const app = new Hono()
app.route('/', api)

Using route

Mount sub-applications to group related routes.
const app = new Hono()

// User routes
const users = new Hono()
users.get('/', (c) => c.json({ users: [] }))
users.get('/:id', (c) => c.json({ user: {} }))
users.post('/', (c) => c.json({ created: true }))

// Post routes
const posts = new Hono()
posts.get('/', (c) => c.json({ posts: [] }))
posts.get('/:id', (c) => c.json({ post: {} }))
posts.post('/', (c) => c.json({ created: true }))

// Mount sub-apps
app.route('/users', users)
app.route('/posts', posts)

// Routes available:
// GET  /users
// GET  /users/:id
// POST /users
// GET  /posts
// GET  /posts/:id
// POST /posts

Router Configuration

Router Options

Configure the router when creating a Hono instance.
import { Hono } from 'hono'
import { RegExpRouter } from 'hono/router/reg-exp-router'
import { TrieRouter } from 'hono/router/trie-router'
import { SmartRouter } from 'hono/router/smart-router'

// Use a specific router
const app = new Hono({ router: new RegExpRouter() })

// Use SmartRouter with custom routers
const app2 = new Hono({
  router: new SmartRouter({
    routers: [new RegExpRouter(), new TrieRouter()]
  })
})

Available Routers

Hono includes several router implementations:

RegExpRouter

Fast regex-based router. Good for most use cases. Default in SmartRouter.

TrieRouter

Trie-based router. Excellent for apps with many routes. Default in SmartRouter.

SmartRouter

Combines multiple routers for optimal performance. Default router in Hono.

LinearRouter

Simple linear search router. Good for simple apps with few routes.

PatternRouter

URLPattern-based router. Uses the web standard URLPattern API.
See the Router API Reference for detailed information about each router.

Strict Mode

Control whether trailing slashes matter in route matching.
// Strict mode enabled (default)
const app = new Hono({ strict: true })
app.get('/about', handler)   // Matches /about only
app.get('/contact/', handler) // Matches /contact/ only

// Strict mode disabled
const app2 = new Hono({ strict: false })
app2.get('/about', handler) // Matches both /about and /about/

Custom Path Function

Customize how paths are extracted from requests.
const app = new Hono({
  getPath: (req) => {
    // Include host header in routing
    const host = req.headers.get('host')
    const path = new URL(req.url).pathname
    return `/${host}${path}`
  }
})

// Route based on host and path
app.get('/api.example.com/users', handler1)
app.get('/admin.example.com/users', handler2)

Route Priority

Matching Order

Routes are matched in the order they are registered.
app.get('/users/me', (c) => c.text('Current user'))
app.get('/users/:id', (c) => c.text('User by ID'))

// GET /users/me → matches first route
// GET /users/123 → matches second route
The order of route registration matters! More specific routes should be registered before generic ones.

Wildcard Priority

Wildcard routes have the lowest priority.
app.get('/api/users', (c) => c.text('Users'))
app.get('/api/*', (c) => c.text('API catch-all'))
app.get('*', (c) => c.text('Global catch-all'))

// GET /api/users → first route
// GET /api/posts → second route
// GET /other → third route

Route Metadata

RouterRoute Interface

Each route is represented by a RouterRoute object.
interface RouterRoute {
  basePath: string
  path: string
  method: string
  handler: H
}

Accessing Routes

Get all registered routes from the app.
const app = new Hono()
app.get('/users', handler)
app.post('/users', handler)

console.log(app.routes)
// [
//   { basePath: '/', path: '/users', method: 'GET', handler: [Function] },
//   { basePath: '/', path: '/users', method: 'POST', handler: [Function] }
// ]

Route Matching

Match Results

The router returns match results containing handlers and parameters.
type Result<T> = 
  | [[T, ParamIndexMap][], ParamStash] 
  | [[T, Params][]]

Parameter Extraction

Parameters are extracted and decoded automatically.
app.get('/users/:id', (c) => {
  // URL: /users/john%20doe
  const id = c.req.param('id') // 'john doe' (decoded)
  return c.json({ id })
})

Best Practices

Register more specific routes before generic wildcard routes to ensure correct matching.
app.get('/users/me', handler1)  // Specific
app.get('/users/:id', handler2) // Less specific
app.get('/users/*', handler3)   // Least specific
Add regex patterns to route parameters to validate input at the routing level.
app.get('/users/:id{[0-9]+}', handler) // Only numeric IDs
For most applications, the default SmartRouter is optimal. Consider specific routers for specialized needs:
  • LinearRouter for very simple apps
  • RegExpRouter for regex-heavy patterns
  • TrieRouter for apps with many routes

Type Safety

Typed Routes

Use TypeScript for type-safe routing.
import { Hono } from 'hono'

type Env = {
  Variables: {
    user: { id: string; name: string }
  }
}

const app = new Hono<Env>()

app.get('/users/:id', (c) => {
  const id = c.req.param('id') // Type-safe string
  const user = c.get('user')   // Type-safe user object
  return c.json(user)
})

Schema Definition

Define API schemas for type-safe client generation.
import { Hono } from 'hono'
import type { Schema } from 'hono'

type AppSchema = {
  '/api/users': {
    GET: {
      response: { users: User[] }
    }
  }
}

const app = new Hono<{}, AppSchema>()

Build docs developers (and LLMs) love