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
The PatternRouter is a straightforward router implementation that converts each route into a regular expression pattern. It’s simple, predictable, and supports most common routing patterns without the complexity of trie structures.
How It Works
The PatternRouter uses a direct approach to routing:
Route Registration : Each route is converted to a RegExp with named capture groups
Pattern Generation : Path segments are transformed into regex patterns
Linear Matching : Routes are tested sequentially until a match is found
Parameter Extraction : Named groups are extracted from the RegExp match
Algorithm Details
Each route stored as a [RegExp, method, handler] tuple
Dynamic parameters (:name) become named capture groups: (?<name>pattern)
Optional parameters (?) create alternate routes
Wildcards (*) are supported in routes
Routes matched in registration order (first match wins for same score)
No preprocessing or optimization
Static Routes O(n) - Linear search through all routes
Dynamic Routes O(n) - Tests each route’s RegExp sequentially
Build Time O(1) - Each route compiled to RegExp immediately
Memory Low - One RegExp per route
When to Use
Small applications with < 50 routes
Simple routing requirements
Applications where route order matters
Prototypes and proof-of-concepts
Learning and understanding routing mechanics
When predictable, simple behavior is preferred over performance
Large applications with 100+ routes (consider RegExpRouter)
Performance-critical applications
High-traffic production APIs
Applications with many static routes (no optimization)
Configuration
To use the PatternRouter, specify it when creating your Hono instance:
import { Hono } from 'hono'
import { PatternRouter } from 'hono/router/pattern-router'
const app = new Hono ({ router: new PatternRouter () })
Usage Examples
Basic Routing
import { Hono } from 'hono'
import { PatternRouter } from 'hono/router/pattern-router'
const app = new Hono ({ router: new PatternRouter () })
// Static routes
app . get ( '/' , ( c ) => c . text ( 'Home' ))
app . get ( '/about' , ( c ) => c . text ( 'About' ))
app . get ( '/contact' , ( c ) => c . text ( 'Contact' ))
// Dynamic routes with parameters
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 })
})
Parameter Patterns
// Parameter with RegExp constraint
app . get ( '/users/:id{[0-9]+}' , ( c ) => {
const id = c . req . param ( 'id' ) // Guaranteed to be numeric
return c . json ({ userId: parseInt ( id ) })
})
// Complex pattern matching
app . get ( '/files/:filename{.+ \\ .(jpg|png|gif)}' , ( c ) => {
const filename = c . req . param ( 'filename' )
return c . json ({ file: filename })
})
// Pattern with multiple constraints
app . get ( '/api/:version{v[1-9]+}/:resource' , ( c ) => {
const { version , resource } = c . req . param ()
return c . json ({ version , resource })
})
Optional Parameters
// Optional trailing parameter
app . get ( '/docs/:page?' , ( c ) => {
const page = c . req . param ( 'page' ) || 'index'
return c . json ({ page })
})
// Optional parameter in path
app . get ( '/api/v1/users/:id?' , ( c ) => {
const id = c . req . param ( 'id' )
if ( id ) {
return c . json ({ user: id })
}
return c . json ({ users: 'all' })
})
// Multiple optional segments
app . get ( '/blog/:year?/:month?/:day?' , ( c ) => {
const { year , month , day } = c . req . param ()
return c . json ({ year , month , day })
})
Wildcard Routes
// Trailing wildcard
app . get ( '/static/*' , ( c ) => {
return c . text ( 'Static file handler' )
})
// Wildcard matches rest of path
app . get ( '/files/public/*' , ( c ) => {
return c . text ( 'Public files' )
})
// Without trailing slash
app . get ( '/api/v1/*' , ( c ) => {
return c . json ({ api: 'v1' , path: c . req . path })
})
Route Order Matters
// ✅ Correct order: Specific routes first
app . get ( '/users/me' , ( c ) => c . json ({ user: 'current' }))
app . get ( '/users/:id' , ( c ) => c . json ({ user: c . req . param ( 'id' ) }))
// GET /users/me → matches first route ✓
// ❌ Wrong order: Generic route shadows specific
app . get ( '/users/:id' , ( c ) => c . json ({ user: c . req . param ( 'id' ) }))
app . get ( '/users/me' , ( c ) => c . json ({ user: 'current' }))
// GET /users/me → matches first route as :id = "me" ✗
How Pattern Matching Works
Pattern Conversion
// Route: '/users/:id/posts/:postId'
// Becomes RegExp: /^\/users\/(?<id>[^/]+)\/posts\/(?<postId>[^/]+)\/?$/
// Route: '/files/:name{[a-z]+.txt}'
// Becomes RegExp: /^\/files\/(?<name>[a-z]+\.txt)\/?$/
// Route: '/api/*'
// Becomes RegExp: /^\/api\//
Matching Process
// Conceptual matching algorithm
function match ( method : string , path : string ) {
const handlers = []
for ( const [ pattern , routeMethod , handler ] of routes ) {
if ( routeMethod === method || routeMethod === 'ALL' ) {
const match = pattern . exec ( path )
if ( match ) {
handlers . push ([ handler , match . groups || {}])
}
}
}
return [ handlers ]
}
Example Flow
app . get ( '/api/users' , handler1 )
app . get ( '/api/:resource' , handler2 )
app . get ( '/api/*' , handler3 )
// Request: GET /api/users
// 1. Test /^\/api\/users\/?$/ → ✓ match → add handler1
// 2. Test /^\/api\/(?<resource>[^/]+)\/?$/ → ✓ match → add handler2
// 3. Test /^\/api\// → ✓ match → add handler3
// Returns: [handler1, handler2, handler3] with appropriate params
Advanced Features
Custom Pattern Syntax
PatternRouter supports custom RegExp patterns within parameters:
// Numeric IDs only
app . get ( '/items/:id{[0-9]+}' , handler )
// Slugs (lowercase, hyphens)
app . get ( '/posts/:slug{[a-z0-9-]+}' , handler )
// Date format (YYYY-MM-DD)
app . get ( '/events/:date{ \\ d{4}- \\ d{2}- \\ d{2}}' , handler )
// UUID format
app . get ( '/resources/:uuid{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}' , handler )
Nested Parameters
// Deep nesting is supported
app . get ( '/:org/projects/:project/issues/:issue' , ( c ) => {
const { org , project , issue } = c . req . param ()
return c . json ({ org , project , issue })
})
// Mix of static and dynamic
app . get ( '/:tenant/api/v1/resources/:id' , ( c ) => {
const { tenant , id } = c . req . param ()
return c . json ({ tenant , resourceId: id })
})
Trailing Slashes
PatternRouter automatically handles trailing slashes:
app . get ( '/users/:id' , handler )
// Both match:
// GET /users/123 ✓
// GET /users/123/ ✓
Limitations
PatternRouter has some limitations:
Linear performance : O(n) for all route types
No optimization : Static routes not faster than dynamic
Order dependent : Route registration order matters
No route conflict detection : Overlapping patterns silently return multiple handlers
Unsupported Patterns
// ❌ These may throw UnsupportedPathError or behave unexpectedly:
// Invalid RegExp patterns
app . get ( '/users/:id{[invalid}' , handler ) // Throws during route registration
// Extremely complex patterns may fail
app . get ( '/complex/:param{(?:..very long pattern..)}' , handler )
Optimize PatternRouter performance:
Put common routes first - They’ll match faster
Minimize total routes - Each route is tested sequentially
Use specific patterns - Reduce false matches
Group by method - Method check happens before pattern matching
Optimization Example
// ✅ Optimized: Most common routes first
app . get ( '/' , homeHandler ) // Most traffic
app . get ( '/about' , aboutHandler ) // Second most
app . get ( '/contact' , contactHandler ) // Third
app . get ( '/users/:id' , userHandler ) // Less common
app . get ( '/*' , notFoundHandler ) // Catch-all last
// ❌ Not optimized: Catch-all first
app . get ( '/*' , catchAll ) // Matches everything!
app . get ( '/' , homeHandler ) // Never reached
Comparison with Other Routers
Feature PatternRouter RegExpRouter TrieRouter LinearRouter Static routes O(n) O(1) O(n) O(n) Dynamic routes O(n) O(1) O(n×m) O(n) Pattern support Good Limited Excellent Excellent Memory usage Low High Medium Low Simplicity High Low Medium High Build time O(1) O(n log n) O(1) O(1)
Internal Architecture
Route Storage
class PatternRouter < T > {
name = 'PatternRouter'
#routes : [ RegExp , string , T ][] = [] // [pattern, method, handler]
}
Add Method
// Simplified add logic
add ( method : string , path : string , handler : T ) {
// Handle wildcards
const endsWithWildcard = path . endsWith ( '*' )
// Handle optional parameters
if ( path . endsWith ( '?' )) {
// Creates additional route without optional part
}
// Convert path to RegExp pattern
const parts = path . match ( / \/ ? ( : \w + (?: { [ ^ } ] + } ) ? ) | \/ ? [ ^ \/\? ] + / g )
const pattern = parts . map ( convertToRegex ). join ( '' )
// Store compiled pattern
this . #routes . push ([ new RegExp ( pattern ), method , handler ])
}
Debugging Routes
const router = new PatternRouter ()
const app = new Hono ({ router })
app . get ( '/users/:id' , handler )
// Access internal routes (for debugging only!)
console . log ( router . routes ) // Array of [RegExp, method, handler]
Source Code Reference
The PatternRouter implementation can be found at:
Router: src/router/pattern-router/router.ts
See Also
LinearRouter Simple router without RegExp compilation
RegExpRouter High-performance compiled router
Routing Guide Learn about choosing the right router
Route Parameters Working with dynamic parameters