Base Path
The basePath() method allows you to create a new Hono instance with a prefixed base path:
import { Hono } from 'hono'
const app = new Hono ()
const api = new Hono (). basePath ( '/api' )
api . get ( '/users' , ( c ) => c . json ({ users: [] }))
api . get ( '/posts' , ( c ) => c . json ({ posts: [] }))
// Mount the api routes
app . route ( '/' , api )
// GET /api/users -> {"users": []}
// GET /api/posts -> {"posts": []}
From src/hono-base.ts:247, basePath() creates a new instance with a merged path:
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
}
Mounting Routes with route()
The route() method allows you to mount a Hono instance at a specific path:
import { Hono } from 'hono'
const app = new Hono ()
const userApp = new Hono ()
userApp . get ( '/' , ( c ) => c . text ( 'User list' ))
userApp . get ( '/:id' , ( c ) => {
const id = c . req . param ( 'id' )
return c . text ( `User ${ id } ` )
})
app . route ( '/users' , userApp )
// GET /users -> "User list"
// GET /users/123 -> "User 123"
Implementation
From src/hono-base.ts:208, route() mounts all routes from the sub-app:
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 > {
const subApp = this . basePath ( path )
app.routes.map((r) => {
let handler
if ( app . errorHandler === errorHandler ) {
handler = r . handler
} else {
handler = async ( c : Context , next : Next ) =>
( await compose ([], app . errorHandler )( c , () => r . handler ( c , next ))). res
;( handler as any )[ COMPOSED_HANDLER ] = r . handler
}
subApp . #addRoute ( r . method , r . path , handler )
})
return this
}
Organizing by Feature
Group related routes into separate modules:
// routes/users.ts
import { Hono } from 'hono'
const users = new Hono ()
users . get ( '/' , ( c ) => c . json ({ users: [] }))
users . post ( '/' , ( c ) => c . json ({ message: 'User created' }))
users . get ( '/:id' , ( c ) => c . json ({ user: { id: c . req . param ( 'id' ) } }))
users . put ( '/:id' , ( c ) => c . json ({ message: 'User updated' }))
users . delete ( '/:id' , ( c ) => c . json ({ message: 'User deleted' }))
export default users
// routes/posts.ts
import { Hono } from 'hono'
const posts = new Hono ()
posts . get ( '/' , ( c ) => c . json ({ posts: [] }))
posts . post ( '/' , ( c ) => c . json ({ message: 'Post created' }))
posts . get ( '/:id' , ( c ) => c . json ({ post: { id: c . req . param ( 'id' ) } }))
export default posts
// index.ts
import { Hono } from 'hono'
import users from './routes/users'
import posts from './routes/posts'
const app = new Hono ()
app . route ( '/users' , users )
app . route ( '/posts' , posts )
export default app
Nested Routing
You can nest routes multiple levels deep:
import { Hono } from 'hono'
const app = new Hono ()
const api = new Hono ()
const v1 = new Hono ()
v1 . get ( '/users' , ( c ) => c . json ({ version: 'v1' , users: [] }))
v1 . get ( '/posts' , ( c ) => c . json ({ version: 'v1' , posts: [] }))
api . route ( '/v1' , v1 )
app . route ( '/api' , api )
// GET /api/v1/users -> {"version": "v1", "users": []}
// GET /api/v1/posts -> {"version": "v1", "posts": []}
Use the hono/route helper to access base path information:
import { Hono } from 'hono'
import { basePath , baseRoutePath } from 'hono/route'
const app = new Hono ()
const userApp = new Hono ()
userApp . get ( '/:id' , ( c ) => {
const base = basePath ( c ) // e.g., '/users'
const route = baseRoutePath ( c ) // e.g., '/users'
return c . json ({ base , route })
})
app . route ( '/users' , userApp )
From src/helper/route/index.ts:106, basePath() returns the base path with embedded parameters:
const basePathCacheMap : WeakMap < Context , Record < number , string >> = new WeakMap ()
export const basePath = ( c : Context , index ?: number ) : string => {
index ??= c . req . routeIndex
const cache = basePathCacheMap . get ( c ) || []
if ( typeof cache [ index ] === 'string' ) {
return cache [ index ]
}
let result : string
const rp = baseRoutePath ( c , index )
if ( ! / [ :* ] / . test ( rp )) {
result = rp
} else {
const paths = splitRoutingPath ( rp )
const reqPath = c . req . path
let basePathLength = 0
for ( let i = 0 , len = paths . length ; i < len ; i ++ ) {
const pattern = getPattern ( paths [ i ], paths [ i + 1 ])
if ( pattern ) {
const re = pattern [ 2 ] === true || pattern === '*' ? / [ ^ \/ ] + / : pattern [ 2 ]
basePathLength += reqPath . substring ( basePathLength + 1 ). match ( re )?.[ 0 ]. length || 0
} else {
basePathLength += paths [ i ]. length
}
basePathLength += 1 // for '/'
}
result = reqPath . substring ( 0 , basePathLength )
}
cache [ index ] = result
basePathCacheMap . set ( c , cache )
return result
}
Using Middleware with Groups
Apply middleware to grouped routes:
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { bearerAuth } from 'hono/bearer-auth'
const app = new Hono ()
const api = new Hono ()
// Apply middleware to all API routes
api . use ( '*' , logger ())
api . use ( '*' , bearerAuth ({ token: 'secret' }))
api . get ( '/users' , ( c ) => c . json ({ users: [] }))
api . get ( '/posts' , ( c ) => c . json ({ posts: [] }))
app . route ( '/api' , api )
// All /api/* routes will have logger and bearerAuth
Grouping with use()
The use() method can apply middleware to multiple routes:
import { Hono } from 'hono'
const app = new Hono ()
// Apply middleware to all /admin routes
app . use ( '/admin/*' , async ( c , next ) => {
// Authentication check
const token = c . req . header ( 'Authorization' )
if ( ! token ) {
return c . text ( 'Unauthorized' , 401 )
}
await next ()
})
app . get ( '/admin/users' , ( c ) => c . json ({ users: [] }))
app . get ( '/admin/settings' , ( c ) => c . json ({ settings: {} }))
From src/hono-base.ts:157, the use() implementation:
this . use = ( arg1 : string | MiddlewareHandler < any >, ... handlers : MiddlewareHandler < any >[]) => {
if ( typeof arg1 === 'string' ) {
this . #path = arg1
} else {
this . #path = '*'
handlers . unshift ( arg1 )
}
handlers . forEach (( handler ) => {
this . #addRoute ( METHOD_NAME_ALL , this . #path , handler )
})
return this as any
}
Versioned APIs
Organize different API versions:
import { Hono } from 'hono'
const app = new Hono ()
// Version 1
const v1 = new Hono ()
v1 . get ( '/users' , ( c ) => c . json ({ version: 1 , users: [] }))
// Version 2 with breaking changes
const v2 = new Hono ()
v2 . get ( '/users' , ( c ) => c . json ({
version: 2 ,
data: { users: [] },
meta: { count: 0 }
}))
app . route ( '/v1' , v1 )
app . route ( '/v2' , v2 )
// GET /v1/users -> version 1 format
// GET /v2/users -> version 2 format
Dynamic Base Paths
Use parameters in base paths for multi-tenant applications:
import { Hono } from 'hono'
import { basePath } from 'hono/route'
const app = new Hono ()
const tenantApp = new Hono ()
tenantApp . get ( '/dashboard' , ( c ) => {
const path = basePath ( c ) // e.g., '/tenant-123'
return c . text ( `Dashboard for ${ path } ` )
})
app . route ( '/:tenant' , tenantApp )
// GET /tenant-123/dashboard -> "Dashboard for /tenant-123"
// GET /acme-corp/dashboard -> "Dashboard for /acme-corp"
Mounting External Applications
For mounting non-Hono applications, use mount():
import { Hono } from 'hono'
const app = new Hono ()
// Mount another framework's handler
app . mount ( '/external' , externalApp . handler )
From src/hono-base.ts:328, the mount() method:
mount (
path : string ,
applicationHandler : ( request : Request , ... args : any ) => Response | Promise < Response > ,
options ?: MountOptions
): Hono < E , S , BasePath , CurrentPath > {
// Handle options
// ...
const handler: MiddlewareHandler = async ( c , next ) => {
const res = await applicationHandler ( replaceRequest ( c . req . raw ), ... getOptions ( c ))
if ( res ) {
return res
}
await next ()
}
this . #addRoute ( METHOD_NAME_ALL , mergePath ( path , '*' ), handler )
return this
}
Best Practices
Group routes by business domain or feature: app . route ( '/users' , usersRoutes )
app . route ( '/products' , productsRoutes )
app . route ( '/orders' , ordersRoutes )
Use consistent base paths
Establish a clear pattern for your API structure: app . route ( '/api/v1/users' , v1UsersRoutes )
app . route ( '/api/v1/posts' , v1PostsRoutes )
Apply middleware at the group level
Share middleware across related routes: const adminRoutes = new Hono ()
adminRoutes . use ( '*' , authMiddleware )
adminRoutes . use ( '*' , adminRoleCheck )
app . route ( '/admin' , adminRoutes )
Each route module should handle a single resource or feature: // users.ts - only user-related routes
// posts.ts - only post-related routes
// comments.ts - only comment-related routes
Route grouping helps maintain clean, modular code and makes it easier to apply middleware and organize your application.