Skip to main content

Overview

The SmartRouter is a meta-router that automatically chooses the optimal routing implementation based on your actual route patterns. It attempts to use the fastest router (RegExpRouter) first, and falls back to more compatible routers (like TrieRouter) if unsupported path patterns are detected.

How It Works

SmartRouter operates through intelligent router selection:
  1. Initial Setup: Accepts an array of routers in priority order
  2. Lazy Evaluation: Routes are collected but not processed until the first match
  3. Auto-Detection: On first request, tries each router in order
  4. Fallback Logic: If a router throws UnsupportedPathError, tries the next one
  5. Optimization: Once a router succeeds, it becomes the active router permanently
  6. Delegation: All future requests go directly to the active router

Algorithm Details

  • Collects routes during application setup (no processing)
  • On first match() call, iterates through router candidates
  • Adds all routes to each candidate router
  • First router that successfully matches becomes the active router
  • Updates its own match method to delegate directly to the active router
  • Zero overhead after the first request

Performance Characteristics

First Request

O(r × n) - r routers × n routes (one-time cost)

Subsequent Requests

O(1) - Direct delegation to active router

Build Time

Lazy - Deferred until first match

Memory

Low → High - Depends on selected router

When to Use

  • Applications where route patterns may vary
  • When you want optimal performance without manual router selection
  • Projects that might add complex routes later
  • Libraries or frameworks that don’t control route patterns
  • Development environments where routes change frequently
  • Applications migrating between different routing strategies

Configuration

The SmartRouter requires explicit router candidates to be provided:
import { Hono } from 'hono'
import { SmartRouter } from 'hono/router/smart-router'
import { RegExpRouter } from 'hono/router/reg-exp-router'
import { TrieRouter } from 'hono/router/trie-router'

const app = new Hono({
  router: new SmartRouter({
    routers: [new RegExpRouter(), new TrieRouter()]
  })
})

Router Priority

Routers are tried in the order provided. Always put faster routers first:
// ✅ Recommended: Fastest first
const router = new SmartRouter({
  routers: [
    new RegExpRouter(),  // Try fastest first
    new TrieRouter(),    // Fallback to most compatible
  ]
})

// ❌ Not optimal: Slower router might be selected unnecessarily
const router = new SmartRouter({
  routers: [
    new TrieRouter(),     // Will always succeed
    new RegExpRouter(),   // Never reached
  ]
})

Usage Examples

Basic Setup

import { Hono } from 'hono'
import { SmartRouter } from 'hono/router/smart-router'
import { RegExpRouter } from 'hono/router/reg-exp-router'
import { TrieRouter } from 'hono/router/trie-router'

const app = new Hono({
  router: new SmartRouter({
    routers: [new RegExpRouter(), new TrieRouter()]
  })
})

// Define routes normally
app.get('/', (c) => c.text('Home'))
app.get('/users/:id', (c) => c.json({ id: c.req.param('id') }))
app.get('/posts/:slug', (c) => c.json({ slug: c.req.param('slug') }))

// SmartRouter will select RegExpRouter (all routes are supported)

Automatic Fallback

import { Hono } from 'hono'
import { SmartRouter } from 'hono/router/smart-router'
import { RegExpRouter } from 'hono/router/reg-exp-router'
import { TrieRouter } from 'hono/router/trie-router'

const app = new Hono({
  router: new SmartRouter({
    routers: [new RegExpRouter(), new TrieRouter()]
  })
})

// Simple routes
app.get('/api/users', getUsers)
app.get('/api/posts', getPosts)

// Complex route that RegExpRouter doesn't support
app.get('/files/:name/*', getFile)  // Wildcard + parameter

// First request: RegExpRouter fails → fallback to TrieRouter
// TrieRouter becomes the active router

Checking Active Router

After the first request, you can check which router was selected:
const router = new SmartRouter({
  routers: [new RegExpRouter(), new TrieRouter()]
})

const app = new Hono({ router })

// Define routes
app.get('/', (c) => c.text('Home'))
app.get('/users/:id', (c) => c.text('User'))

// Before first request
try {
  console.log(router.activeRouter.name)  // Throws error
} catch (e) {
  console.log('No active router yet')
}

// After first request
await app.fetch(new Request('http://localhost/'))
console.log(router.name)  // "SmartRouter + RegExpRouter"
console.log(router.activeRouter.name)  // "RegExpRouter"

Custom Router Chain

import { SmartRouter } from 'hono/router/smart-router'
import { RegExpRouter } from 'hono/router/reg-exp-router'
import { TrieRouter } from 'hono/router/trie-router'
import { PatternRouter } from 'hono/router/pattern-router'

// Try multiple routers in priority order
const router = new SmartRouter({
  routers: [
    new RegExpRouter(),   // Fastest, but limited patterns
    new PatternRouter(),  // Moderate speed, more patterns
    new TrieRouter(),     // Slowest, all patterns supported
  ]
})

const app = new Hono({ router })

How Selection Works

Selection Flow

// Conceptual selection algorithm
function selectRouter(routers, routes, method, path) {
  for (const router of routers) {
    try {
      // Add all routes to candidate router
      for (const [method, path, handler] of routes) {
        router.add(method, path, handler)
      }
      
      // Try to match the incoming request
      const result = router.match(method, path)
      
      // Success! This router supports all our routes
      return { router, result }
    } catch (error) {
      if (error instanceof UnsupportedPathError) {
        // This router can't handle our routes, try next
        continue
      }
      throw error  // Unexpected error, propagate
    }
  }
  
  throw new Error('No compatible router found')
}

Example Selection Scenarios

// Routes:
app.get('/', handler)
app.get('/users/:id', handler)
app.get('/posts/:slug', handler)

// Result: RegExpRouter selected
// Reason: All routes are supported, best performance

Advanced Features

Name Reporting

SmartRouter updates its name to reflect the active router:
const router = new SmartRouter({
  routers: [new RegExpRouter(), new TrieRouter()]
})

console.log(router.name)  // "SmartRouter"

// After first request
await app.fetch(request)
console.log(router.name)  // "SmartRouter + RegExpRouter"

Zero-Overhead Delegation

After selection, SmartRouter replaces its own match method:
// Before first request
smartRouter.match(method, path)  // Runs selection logic

// After first request
smartRouter.match(method, path)  // Direct delegation to active router
// No performance penalty!

Route Collection

SmartRouter stores routes without processing them:
const router = new SmartRouter({ routers: [new RegExpRouter()] })
const app = new Hono({ router })

// These calls are very fast - just collecting data
app.get('/route1', handler1)  // Stored
app.get('/route2', handler2)  // Stored
app.get('/route3', handler3)  // Stored
// No trie building, no RegExp compilation yet!

// First request triggers selection and building
await app.fetch(request)  // Now routes are processed

Error Handling

No Compatible Router

const router = new SmartRouter({
  routers: [new RegExpRouter()]  // Only one router
})

const app = new Hono({ router })

// Add route that RegExpRouter doesn't support
app.get('/complex/:param/*', handler)

// First request will fail
try {
  await app.fetch(request)
} catch (error) {
  console.error('Fatal error: No compatible router found')
}

Accessing Active Router Too Early

const router = new SmartRouter({
  routers: [new RegExpRouter(), new TrieRouter()]
})

// Before any requests
try {
  const active = router.activeRouter
} catch (error) {
  // Error: "No active router has been determined yet."
}

Best Practices

1

Order Routers by Speed

Put faster routers first in the array. RegExpRouter should typically be first.
routers: [new RegExpRouter(), new TrieRouter()]  // ✅ Good
routers: [new TrieRouter(), new RegExpRouter()]  // ❌ Suboptimal
2

Include a Fallback Router

Always include a router that supports all patterns (like TrieRouter) as the last option.
routers: [
  new RegExpRouter(),  // Try fastest
  new TrieRouter(),    // Fallback to most compatible
]
3

Monitor Active Router

In production, log which router was selected to understand your routing patterns.
app.use('*', async (c, next) => {
  await next()
  if (router.name !== 'SmartRouter') {
    console.log('Active router:', router.activeRouter.name)
  }
})

Comparison with Direct Router Usage

AspectSmartRouterDirect Router
First requestSlower (selection)Normal speed
Subsequent requestsSame as directBaseline
Route compatibilityAutomatic fallbackManual selection
FlexibilityHighLow
PredictabilityLowerHigher
ComplexityLower (auto)Higher (manual)

Performance Considerations

The first request has additional overhead:
  • Route duplication: All routes added to each candidate router
  • Multiple attempts: Each router tried until one succeeds
  • Build time: Selected router builds its internal structures
For production APIs with strict latency requirements on first request, consider using a router directly.

Optimization Strategies

// Strategy 1: Pre-warm the router during startup
const router = new SmartRouter({
  routers: [new RegExpRouter(), new TrieRouter()]
})
const app = new Hono({ router })

// Define all routes
app.get('/', handler)
app.get('/users/:id', handler)

// Trigger router selection before accepting traffic
await app.fetch(new Request('http://localhost/'))
console.log('Router selected:', router.activeRouter.name)
// Now ready for production traffic!

// Strategy 2: Use SmartRouter in development, direct router in production
const router = process.env.NODE_ENV === 'production'
  ? new RegExpRouter()  // Known to work with our routes
  : new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] })

Source Code Reference

The SmartRouter implementation can be found at:
  • Router: src/router/smart-router/router.ts

See Also

RegExpRouter

High-performance trie-based RegExp router

TrieRouter

Tree-based router supporting all patterns

Routing Guide

Learn about choosing the right router

Performance Guide

Optimize your Hono application

Build docs developers (and LLMs) love