π A zero-magic, file-based router for Hono. Just export a Hono app from
routes/**/route.tsand it mounts automatically.
Most existing file-based routers for Hono introduce new conventions (like GET.ts/POST.ts) or extra abstractions.
hono-autoroutes takes a different path:
- πͺ No new APIs β use plain Hono as-is.
- π File-based discovery β place
route.tsfiles underroutes/. - π Flexible contract β export a
Honoinstance or aregister(app)/createRoutes(app)function. - π Works everywhere β Node/Bun (filesystem scan) and Edge/Workers (via
import.meta.glob). - π§© Scoped middleware β optional
middleware.tsat any folder applies to that path and all children.
npm install hono-autoroutes
# or
bun add hono-autoroutes
pnpm add hono-autoroutesProject structure:
src/
app.ts
routes/
route.ts
users/
route.ts
posts/
route.ts
import { Hono } from 'hono'
import { mountAutoRoutes } from 'hono-autoroutes'
const app = new Hono()
await mountAutoRoutes(app, {
rootDir: 'src/routes', // optional, defaults to src/routes or routes
})
export default appimport { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello root!'))
export default appimport { Hono } from 'hono'
const app = new Hono()
app.get('/:id', (c) => c.json({ user: c.req.param('id') }))
export default appCreate middleware.ts files alongside your routes to apply middleware to that folder and all of its children. Middleware is applied to both the exact folder path and its subtree.
src/
routes/
middleware.ts # applies to /* (entire tree)
users/
middleware.ts # applies to /users and /users/*
route.ts
admin/
logs/
middleware.ts # applies to /admin/logs and /admin/logs/*
Supported exports:
// default: single or array of middlewares
export default async (c, next) => { /* ... */ await next() }
// or
export default [mw1, mw2]
// named
export const middleware = (c, next) => next()
export const middlewares = [mw1, mw2]
// register-style: calls are automatically scoped to the folder
export function register(app: { use: (...args: any[]) => void }) {
// behaves like: app.use('/folder', mw) and app.use('/folder/*', mw)
app.use((c, next) => next())
}Since Edge environments canβt read the filesystem, you can provide an entries map (e.g. via import.meta.glob).
import { Hono } from 'hono'
import { mountAutoRoutesFromEntries } from 'hono-autoroutes'
const app = new Hono()
const entries = import.meta.glob('/src/routes/**/route.ts')
await mountAutoRoutesFromEntries(app, entries, {
virtualRoot: '/src/routes', // strip this prefix to derive mount paths
})
export default appProvide middleware files in your entries map as well; theyβll be applied to both exact and wildcard paths.
const entries = {
...import.meta.glob('/src/routes/**/route.ts'),
...import.meta.glob('/src/routes/**/middleware.ts'),
}
await mountAutoRoutesFromEntries(app, entries, { virtualRoot: '/src/routes' })Each route.ts file can export in one of these forms:
- Default export: Hono instance (preferred)
export default new Hono().get('/', (c) => c.text('hi'))- Named export:
register(app)
export function register(app: Hono) {
app.get('/', (c) => c.text('hi'))
}- Named export:
createRoutes(app)
export async function createRoutes(app: Hono) {
app.get('/', (c) => c.text('hi'))
}type AutoroutesOptions = {
rootDir?: string
fileNames?: string[] | RegExp
fileName?: string // deprecated
middlewareFileNames?: string[] | RegExp
middlewareFileName?: string // deprecated
entries?: Record<string, any | (() => Promise<any>)>
virtualRoot?: string | RegExp
logger?: { log?: (msg: string) => void; warn?: (msg: string) => void }
duplicateStrategy?: 'first' | 'last'
}- rootDir β Path to routes folder (default:
src/routesorroutes). - fileNames β Allowed route filenames (default:
route.ts/js/mjs/cjs). - entries β Bundler-provided modules (Edge mode).
- virtualRoot β Root prefix to strip when deriving mount paths.
- logger β Custom logging implementation.
- duplicateStrategy β If multiple files map to the same path, keep the
first(default) or thelast(applies to routes and middleware). - middlewareFileNames β Allowed middleware filenames (default:
middleware.ts/js/mjs/cjs).
routes/route.tsβ/routes/users/route.tsβ/usersroutes/admin/route.tsβ/adminroutes/middleware.tsβ applies to all routes (i.e./*)routes/admin/middleware.tsβ applies to/admin/*
- Node/Bun: uses
fsto walk the routes folder. - Edge/Workers: must use
entries(e.g.import.meta.glob). - Duplicate mount paths are warned; strategy can be controlled.
- No extra conventions: you still define routes with Hono APIs (
app.get,app.post, etc.). - Middleware modules can export:
- default function (single middleware) or array of middlewares
- named:
middlewareormiddlewares - or
register(app)/createMiddleware(app), whereapp.use()calls are scoped to that folder
- Middleware covers the exact folder path (e.g.
/users) and the subtree (e.g./users/*).
MIT Β© 2025