new Hono(options) accepts exactly three options, defined as HonoOptions in src/hono-base.ts. There is no config file, no environment variable, no plugin registry -- all other behavior is set through methods (onError, notFound, use) or the choice of entry point (hono, hono/quick, hono/tiny).
export type HonoOptions<E extends Env> = {
/**
* `strict` option specifies whether to distinguish whether the last path is a directory or not.
* @default true
*/
strict?: boolean
/**
* `router` option specifies which router to use.
*/
router?: Router<[H, RouterRoute]>
/**
* `getPath` can handle the host header value.
*/
getPath?: GetPath<E>
}
strict (default: true)
Controls whether /hello and /hello/ are different routes. Implementation-wise it is nothing more than a choice of path extractor in the constructor:
this.getPath = (strict ?? true) ? (options.getPath ?? getPath) : getPathNoStrict
getPathNoStrict strips one trailing slash before matching:
export const getPathNoStrict = (request: Request): string => {
const result = getPath(request)
// if strict routing is false => `/hello/hey/` and `/hello/hey` are treated the same
return result.length > 1 && result.at(-1) === '/' ? result.slice(0, -1) : result
}
The pinned behavior, from the test suite:
describe('strict is false', () => {
const app = new Hono({ strict: false })
app.get('/hello', (c) => {
return c.text('/hello')
})
it('/hello and /hello/ are treated as the same', async () => {
let res = await app.request('http://localhost/hello')
// …status 200
res = await app.request('http://localhost/hello/')
// …status 200
})
})
With the default strict: true, the same suite asserts /hello/ returns 404 when only /hello is registered (and vice versa). Note that strict: false normalizes the request path only -- register routes without trailing slashes.
router
Replaces the default SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }) with any object implementing the two-method Router interface. Pinned by test:
it('Should be RegExpRouter', () => {
const app = new Hono({
router: new RegExpRouter(),
})
expect(app.router instanceof RegExpRouter).toBe(true)
})
Use cases and trade-offs per router are in Routing. Remember the shared constraint: SmartRouter-based apps cannot register routes after the first request has been matched.
getPath
Replaces path extraction entirely, receiving the raw Request (and { env }). The JSDoc's own example enables host-based routing by prefixing the host header onto the matched path:
* const app = new Hono({
* getPath: (req) =>
* '/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
* })
*
* app.get('/www1.example.com/hello', () => c.text('hello www1'))
If you pass both strict: false and a custom getPath, the custom getPath is ignored in favor of getPathNoStrict -- visible in the constructor line quoted above, and pinned by the strict is false with getPath option case in src/hono.test.ts, which passes getPath explicitly and still observes non-strict matching.
Not configuration, but adjacent
app.basePath('/api')-- prefixes all subsequently registered routes; returns a clone (see Composing apps).app.onError(handler)/app.notFound(handler)-- replace the default 500/404 responders (see Errors and not-found).strictinteracts only with matching; it never rewrites the URL a handler sees viac.req.path.