---
title: "Application Core"
description: "The application core of Hono centers around the Hono class and its foundational base HonoBase in [[Application Core]], serving as the central dispatcher and routing engine that handles request life..."
last_updated: "2026-10-12T08:14:12.710392+00:00"
canonical_url: "https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/application-core"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [src/hono-base.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts)
- [src/types.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/types.ts)
- [src/hono.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono.ts)
- [src/helper/route/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/route/index.ts)
- [src/preset/quick.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/preset/quick.ts)
- [src/context.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/context.ts)
- [src/preset/tiny.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/preset/tiny.ts)
- [src/request.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/request.ts)
- [src/router/trie-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/trie-router/index.ts)
- [src/router/smart-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/smart-router/index.ts)
- [src/router/linear-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/linear-router/index.ts)
- [src/router/pattern-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/pattern-router/index.ts)
</details>

## Overview

The application core of Hono centers around the `Hono` class and its foundational base `HonoBase` in `[[Application Core]]`, serving as the central dispatcher and routing engine that handles [request lifecycle](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/request-lifecycle) execution, path matching, and middleware coordination across Web Standard runtimes. By separating core routing logic from distribution builds like `[[Presets and Builds]]`, Hono provides an extensible architecture where applications can inject optimized routing strategies such as `SmartRouter`, `TrieRouter`, `LinearRouter`, or `PatternRouter` to resolve endpoints and parameters. Incoming requests are wrapped into an [Execution Context](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/execution-context) via `Context` and `HonoRequest` objects, which manage response rendering, environment bindings, and scoped basePath transformations while supporting modular sub-application composition and centralized error handling.

Sources: [src/hono-base.ts:98-126](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L98-L126), [src/hono.ts:16-33](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono.ts#L16-L33), [src/context.ts:293-361](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/context.ts#L293-L361), [src/request.ts:36-80](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/request.ts#L36-L80)

## Hono and HonoBase architecture

### Class Hierarchy and Core Separation

The application architecture relies on a two-tier class hierarchy separating abstract routing mechanisms from concrete router initialization. `HonoBase` acts as an abstract-like engine implementing route registration, method assignment, [middleware composition](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/middleware-composition), request dispatching, and error handling without binding itself to a specific router implementation. The concrete `Hono` class extends `HonoBase`, supplying default router configurations and initializing application options.

```typescript
class Hono<
  E extends Env = Env,
  S extends Schema = {},
  BasePath extends string = '/',
  CurrentPath extends string = BasePath,
> {
  get!: HandlerInterface<E, 'get', S, BasePath, CurrentPath>
  post!: HandlerInterface<E, 'post', S, BasePath, CurrentPath>
  put!: HandlerInterface<E, 'put', S, BasePath, CurrentPath>
  delete!: HandlerInterface<E, 'delete', S, BasePath, CurrentPath>
  options!: HandlerInterface<E, 'options', S, BasePath, CurrentPath>
  patch!: HandlerInterface<E, 'patch', S, BasePath, CurrentPath>
  all!: HandlerInterface<E, 'all', S, BasePath, CurrentPath>
  on: OnHandlerInterface<E, S, BasePath>
  use: MiddlewareHandlerInterface<E, S, BasePath>

  /*
    This class is like an abstract class and does not have a router.
    To use it, inherit the class and implement router in the constructor.
  */
  router!: Router<[H, RouterRoute]>
  readonly getPath: GetPath<E>
  // Cannot use `#` because it requires visibility at JavaScript runtime.
  private _basePath: string = '/'
  #path: string = '/'

  routes: RouterRoute[] = []

  constructor(options: HonoOptions<E> = {}) {
```
Sources: [src/hono-base.ts:98-126](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L98-L126)

```typescript
export class Hono<
  E extends Env = BlankEnv,
  S extends Schema = BlankSchema,
  BasePath extends string = '/',
> extends HonoBase<E, S, BasePath> {
  /**
   * Creates an instance of the Hono class.
   *
   * @param options - Optional configuration options for the Hono instance.
   */
  constructor(options: HonoOptions<E> = {}) {
    super(options)
    this.router =
      options.router ??
      new SmartRouter({
        routers: [new RegExpRouter(), new TrieRouter()],
      })
  }
```
Sources: [src/hono.ts:16-33](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono.ts#L16-L33)

> [!NOTE]
> `HonoBase` declares `router` without instantiating it directly in its own constructor. Subclasses or custom builds must inject a valid `Router` implementation before routing requests, or errors will occur during matching.

### Constructor Options and Router Injection

The constructor accepts configuration options via `HonoOptions`, allowing developers to override default path resolution strategies or inject custom routing engines that conform to the [Router Contracts](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/routing/router-contracts) specification.

| Option Key | Type | Default Value | Purpose |
| :--- | :--- | :--- | :--- |
| `router` | `Router<[H, RouterRoute]>` | `new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] })` | Custom router instance injected into the application. |
| `strict` | `boolean` | `true` | Controls whether trailing slashes are strictly matched during path resolution. |
| `getPath` | `GetPath<E>` | `getPath` (strict) or `getPathNoStrict` | Custom function to extract the pathname from incoming request objects. |

```typescript
  const { strict, ...optionsWithoutStrict } = options
  Object.assign(this, optionsWithoutStrict)
  this.getPath = (strict ?? true) ? (options.getPath ?? getPath) : getPathNoStrict
}
```
Sources: [src/hono-base.ts:170-173](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L170-L173)

```typescript
constructor(options: HonoOptions<E> = {}) {
  super(options)
  this.router =
    options.router ??
    new SmartRouter({
      routers: [new RegExpRouter(), new TrieRouter()],
    })
}
```
Sources: [src/hono.ts:26-33](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono.ts#L26-L33)

> [!TIP]
> Setting `strict: false` in `HonoOptions` automatically swaps the default path parser from `getPath` to `getPathNoStrict`, allowing requests with and without trailing slashes to resolve to the same handler.

### Default Router Configuration

When instantiated without parameters, the concrete `Hono` constructor defaults to `SmartRouter`, configured with an internal fallback array containing `RegExpRouter` and `TrieRouter` instances. This composition allows Hono to dynamically evaluate route patterns and delegate matching to the most efficient underlying router strategy.

```typescript
constructor(options: HonoOptions<E> = {}) {
  super(options)
  this.router =
    options.router ??
    new SmartRouter({
      routers: [new RegExpRouter(), new TrieRouter()],
    })
}
```
Sources: [src/hono.ts:26-33](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono.ts#L26-L33)

## HTTP method handler registration

### Method and Route Registration APIs

The `HonoBase` class exposes standard HTTP method registration properties including `get`, `post`, `put`, `delete`, `options`, `patch`, and `all`. Each method is initialized dynamically in the constructor by iterating over `METHODS` and `METHOD_NAME_ALL_LOWERCASE`. When invoked, these method properties accept either a path string followed by one or more request handlers, or simply a sequence of handlers when a path has been set previously. 

```typescript
const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE]
allMethods.forEach((method) => {
  this[method] = (args1: string | H, ...args: H[]) => {
    if (typeof args1 === 'string') {
      this.#path = args1
    } else {
      this.#addRoute(method, this.#path, args1)
    }
    args.forEach((handler) => {
      this.#addRoute(method, this.#path, handler)
    })
    return this as any
  }
})
```
Sources: [src/hono-base.ts:128-141](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L128-L141)

> [!NOTE]
> If the first argument passed to a method handler like `app.get()` is a string, it updates the internal `#path` property for that registration context. Subsequent arguments and subsequent calls without a string path attach the provided handlers to that stored path.

### Custom Method Registration via app.on

In addition to standard method shortcuts, `HonoBase` provides the `.on()` method to register routes for arbitrary HTTP methods or collections of methods and paths simultaneously. The `.on()` implementation flattens input arrays for both paths and methods, converting method strings to uppercase via `.toUpperCase()` before adding them to the underlying router.

```typescript
// Implementation of app.on(method, path, ...handlers[])
this.on = (method: string | string[], path: string | string[], ...handlers: H[]) => {
  for (const p of [path].flat()) {
    this.#path = p
    for (const m of [method].flat()) {
      handlers.map((handler) => {
        this.#addRoute(m.toUpperCase(), this.#path, handler)
      })
    }
  }
  return this as any
}
```
Sources: [src/hono-base.ts:143-154](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L143-L154)

> [!WARNING]
> Passing lowercase method strings to `app.on()` is automatically normalized via `toUpperCase()`, but callers must ensure custom method identifiers comply with valid HTTP specification requirements if used outside standard fetch client constraints.

### Type Signatures and Handler Overloads

Type safety across route registration is governed by `HandlerInterface` and `OnHandlerInterface`, which support multiple overloaded signatures ranging from single handlers to multi-handler middleware chains up to ten handlers deep. These interfaces track environment variables (`Env`), path parameters, input schemas (`Input`), and typed response mappings (`HandlerResponse`), merging them into the application's aggregate `Schema`.

```typescript
      MiddlewareHandler<E6, P>,
      MiddlewareHandler<E7, P>,
      MiddlewareHandler<E8, P>,
      MiddlewareHandler<E9, P>,
      MiddlewareHandler<E10, P>,
    ]
  ): HonoBase<
    IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>,
    S,
    BasePath,
    MergedPath
  >

  //// app.use(path, ...handlers[])
  <P extends string, E2 extends Env = E>(
    path: P,
    ...handlers: MiddlewareHandler<E2, MergePath<BasePath, P>>[]
  ): HonoBase<E, S, BasePath, MergePath<BasePath, P>>
}
```
Sources: [src/types.ts:1431-1449](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/types.ts:1431-1449)

```typescript
  M5 extends H<E6, MergedPath, I5> = H<E6, MergedPath, I5>,
  M6 extends H<E7, MergedPath, I6> = H<E7, MergedPath, I6>,
  M7 extends H<E8, MergedPath, I7> = H<E8, MergedPath, I7>,
  M8 extends H<E9, MergedPath, I8> = H<E9, MergedPath, I8>,
  M9 extends H<E10, MergedPath, I9> = H<E10, MergedPath, I9>,
>(
  method: M,
  path: P,
  ...handlers: [
    H<E2, MergedPath, I> & M1,
    H<E3, MergedPath, I2> & M2,
    H<E4, MergedPath, I3> & M3,
    H<E5, MergedPath, I4> & M4,
    H<E6, MergedPath, I5> & M5,
    H<E7, MergedPath, I6> & M6,
    H<E8, MergedPath, I7> & M7,
    H<E9, MergedPath, I8> & M8,
    H<E10, MergedPath, I9> & M9,
    H<E11, MergedPath, I10, R>,
  ]
): HonoBase<
  IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11]>,
  S &
    ToSchema<
      M,
      MergePath<BasePath, P>,
      I10,
      | MergeTypedResponse<R>
      | MergeMiddlewareResponse<M1>
      | MergeMiddlewareResponse<M2>
      | MergeMiddlewareResponse<M3>
      | MergeMiddlewareResponse<M4>
      | MergeMiddlewareResponse<M5>
      | MergeMiddlewareResponse<M6>
      | MergeMiddlewareResponse<M7>
      | MergeMiddlewareResponse<M8>
      | MergeMiddlewareResponse<M9>
    >,
  BasePath,
  MergePath<BasePath, P>
>
```
Sources: [src/types.ts:1901-1941](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/types.ts:1901-1941)

## Route storage and path resolution

### Internal Route Structure and Routing Tables

When handlers are registered through method shortcuts, `app.use()`, or `app.on()`, `HonoBase` invokes its private `#addRoute` method to normalize paths, uppercase method identifiers, and populate internal routing structures. Every registered route is encapsulated as a `RouterRoute` object containing the assigned `basePath`, the fully qualified `path`, the HTTP `method`, and the target `handler`. 

```typescript
#addRoute(method: string, path: string, handler: H, baseRoutePath?: string): void {
  method = method.toUpperCase()
  path = mergePath(this._basePath, path)
  const r: RouterRoute = {
    basePath:
      baseRoutePath !== undefined ? mergePath(this._basePath, baseRoutePath) : this._basePath,
    path,
    method,
    handler,
  }
  this.router.add(method, path, [handler, r])
  this.routes.push(r)
}
```
Sources: [src/hono-base.ts:385-397](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L385-L397)

The `RouterRoute` entries are dual-stored: they are pushed sequentially into the instance's public `routes` array for introspection or sub-application composition, and they are registered directly into the active `router` instance by calling `this.router.add(method, path, [handler, r])`. This associates each route pattern with a tuple containing the handler and its metadata description.

```source
src/hono-base.ts:124|   routes: RouterRoute[] = []
```
Sources: [src/hono-base.ts:124](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L124)

> [!NOTE]
> The router implementation stores tuples of `[handler, r]` as values for each registered method and path combination, permitting downstream dispatch logic to access both the executable function and the complete `RouterRoute` descriptor during request matching.

### Path Normalization and Prefix Merging

Path normalization and prefix combining are handled by utility functions imported from `./utils/url`, specifically `mergePath`, `getPath`, and `getPathNoStrict`. Within `#addRoute`, incoming paths are passed through `mergePath(this._basePath, path)` to ensure that any configured application base path prefixes are correctly prepended without redundant slashes or missing separators.

```typescript
method = method.toUpperCase()
path = mergePath(this._basePath, path)
```
Sources: [src/hono-base.ts:386-387](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L386-L387)

The active `getPath` function is selected during instantiation based on the `strict` option flag. If `strict` evaluates to true (the default), `options.getPath ?? getPath` is selected, enforcing exact path matching including trailing slashes. If `strict` is set to false, `getPathNoStrict` is assigned to normalize out trailing slashes during path resolution.

```typescript
  const { strict, ...optionsWithoutStrict } = options
  Object.assign(this, optionsWithoutStrict)
  this.getPath = (strict ?? true) ? (options.getPath ?? getPath) : getPathNoStrict
}
```
Sources: [src/hono-base.ts:170-173](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L170-L173)

> [!CAUTION]
> Modifying the `strict` flag alters how `getPath` processes incoming URLs at runtime; mixing strict and non-strict sub-applications without accounting for trailing slash normalization can lead to unexpected `404 Not Found` responses during lookup matching.

## Sub-application routing and basePath

### Scoped basePath Cloning and Instance Isolation

Modular composition in Hono relies on creating isolated sub-applications and scoping routing prefixes via `.basePath()`. The `.basePath()` method invokes a private `#clone()` helper that instantiates a new Hono instance sharing the parent's underlying `router`, `getPath`, `errorHandler`, `#notFoundHandler`, and `routes` references, while updating the private `_basePath` property through `mergePath(this._basePath, path)`.

```typescript
#clone(): Hono<E, S, BasePath, CurrentPath> {
  const clone = new Hono<E, S, BasePath, CurrentPath>({
    router: this.router,
    getPath: this.getPath,
  })
  clone.errorHandler = this.errorHandler
  clone.#notFoundHandler = this.#notFoundHandler
  clone.routes = this.routes
  return clone
}
```
Sources: [src/hono-base.ts:175-184](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L175-L174)

```typescript
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
}
```
Sources: [src/hono-base.ts:247-253](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L247-L253)

> [!TIP]
> Calling `.basePath()` does not duplicate the underlying router or existing route collection; instead, it returns a lightweight shadow instance pre-configured with an extended `_basePath` value for subsequent route registrations.

### Composing Sub-Applications with route()

Sub-applications and independent Hono router instances can be merged into a parent application using the `.route()` method. When `.route(path, app)` is invoked, it creates a scoped sub-application at the specified mount path and iterates over all `app.routes` definitions, re-registering each route into the parent's router and route collection.

```typescript
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, r.basePath)
  })
  return this
}
```
Sources: [src/hono-base.ts:208-232](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L208-L232)

If the mounted sub-application defines a custom error handler differing from the default `errorHandler`, `.route()` wraps each copied route handler in a composed execution block that routes errors through the sub-application's specific error boundary before falling back or continuing execution.

```typescript
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
}
```
Sources: [src/hono-base.ts:221-227](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L221-L227)

### External Framework Mounting with mount()

External request handlers and applications built with other frameworks can be integrated into a Hono application using the `.mount()` method. It processes optional `MountOptions` configurations, which accept either a custom `MountOptionHandler` function or an options object containing an `optionHandler` and a `replaceRequest` strategy.

```typescript
mount(
  path: string,
  applicationHandler: (request: Request, ...args: any) => Response | Promise<Response>,
  options?: MountOptions
): Hono<E, S, BasePath, CurrentPath> {
  // handle options
  let replaceRequest: MountReplaceRequest | undefined
  let optionHandler: MountOptionHandler | undefined
  if (options) {
    if (typeof options === 'function') {
      optionHandler = options
    } else {
      optionHandler = options.optionHandler
      if (options.replaceRequest === false) {
        replaceRequest = (request) => request
      } else {
        replaceRequest = options.replaceRequest
      }
    }
  }
```
Sources: [src/hono-base.ts:328-347](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L328-L347)

The request transformation and argument extraction behavior during execution depend on whether explicit options are provided:

| Option Property | Default Behavior | Custom Behavior |
| :--- | :--- | :--- |
| `replaceRequest` | Strips the mounted path prefix from the incoming request URL pathname | Passes the request through a custom `MountReplaceRequest` function or preserves it unchanged if set to `false` |
| `optionHandler` | Passes `[c.env, executionContext]` extracted from the active `Context` | Invokes the custom `MountOptionHandler` function and normalizes its return value into an array |

```typescript
const getOptions: (c: Context) => unknown[] = optionHandler
  ? (c) => {
      const options = optionHandler!(c)
      return Array.isArray(options) ? options : [options]
    }
  : (c) => {
      let executionContext: ExecutionContext | undefined = undefined
      try {
        executionContext = c.executionCtx
      } catch {} // Do nothing
      return [c.env, executionContext]
    }
replaceRequest ||= (() => {
  const mergedPath = mergePath(this._basePath, path)
  const pathPrefixLength = mergedPath === '/' ? 0 : mergedPath.length
  return (request) => {
    const url = new URL(request.url)
    url.pathname = this.getPath(request).slice(pathPrefixLength) || '/'
    return new Request(url, request)
  }
})()
```
Sources: [src/hono-base.ts:350-370](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L350-L370)

Once the request transformer and argument extractors are resolved, `.mount()` registers a wildcard route matching `mergePath(path, '*')` with `METHOD_NAME_ALL`, executing the external handler inside a middleware wrapper.

```typescript
  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
}
```
Sources: [src/hono-base.ts:372-383](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L372-L383)

## Application-level error handling

### Default Handlers and Customization

Hono provides built-in fallback handlers for unhandled routing misses and runtime exceptions. The default `notFoundHandler` returns a plain text response with status code `404` and the body `404 Not Found`.

```typescript
const notFoundHandler: NotFoundHandler = (c) => {
  return c.text('404 Not Found', 404)
}
```
Sources: [src/hono-base.ts:31-33](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L31-L33)

The default `errorHandler` inspects caught exceptions to check if they implement a `getResponse()` method. If present, it directly returns the response provided by the error instance; otherwise, it formats a generic internal error response.

```typescript
const errorHandler: ErrorHandler = (err, c) => {
  if ('getResponse' in err) {
    const res = err.getResponse()
    return c.newResponse(res.body, res)
  }
  console.error(err)
  return c.text('Internal Server Error', 500)
```
Sources: [src/hono-base.ts:35-41](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L35-L41)

Applications can override these default behaviors by registering custom handlers via the `.notFound()` and `.onError()` methods, which update the instance's `#notFoundHandler` and `errorHandler` properties and return the modified Hono instance.

```typescript
onError = (handler: ErrorHandler<E>): Hono<E, S, BasePath, CurrentPath> => {
  this.errorHandler = handler
  return this
}

/**
 * `.notFound()` allows you to customize a Not Found Response.
 *
 * @see {@link https://hono.dev/docs/api/hono#not-found}
 *
 * @param {NotFoundHandler} handler - request handler for not-found
 * @returns {Hono} changed Hono instance
 *
 * @example
 * ```ts
 * app.notFound((c) => {
 *   return c.text('Custom 404 Message', 404)
 * })
 * ```
 */
notFound = (handler: NotFoundHandler<E>): Hono<E, S, BasePath, CurrentPath> => {
  this.#notFoundHandler = handler
  return this
}
```
Sources: [src/hono-base.ts:271-294](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L271-L294)

### Exception Dispatching and Error Normalization

During request dispatching in `#dispatch()`, runtime exceptions thrown within route handlers or middleware are caught and passed to `#handleError()`. The `#handleError()` method checks if the thrown value is an instance of `Error`; if so, it delegates to the registered `errorHandler`. If the thrown value is not an `Error` instance, it rethrows the raw value.

```typescript
#handleError(err: unknown, c: Context<E>): Response | Promise<Response> {
  if (err instanceof Error) {
    return this.errorHandler(err, c)
  }
  throw err
}
```
Sources: [src/hono-base.ts:399-404](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L399-L404)

> [!WARNING]
> Non-`Error` exceptions (such as thrown strings or primitives) bypass the custom `errorHandler` entirely and propagate out of `#handleError()`, potentially causing unhandled promise rejections if not caught at the top-level fetch boundary.

When dispatching requests with a single matching handler or via composed middleware chains, exceptions originating from synchronous code execution, asynchronous promise rejections, or unfinalized context states are intercepted and routed through the error handling pipeline.

```typescript
  if (matchResult[0].length === 1) {
    let res: ReturnType<H>
    try {
      res = matchResult[0][0][0][0](c, async () => {
        c.res = await this.#notFoundHandler(c)
      })
    } catch (err) {
      return this.#handleError(err, c)
    }

    return res instanceof Promise
      ? res
          .then(
            (resolved: Response | undefined) =>
              resolved || (c.finalized ? c.res : this.#notFoundHandler(c))
          )
          .catch((err: Error) => this.#handleError(err, c))
      : (res ?? this.#notFoundHandler(c))
  }

  const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler)

  return (async () => {
    try {
      const context = await composed(c)
      if (!context.finalized) {
        throw new Error(
          'Context is not finalized. Did you forget to return a Response object or `await next()`?'
        )
      }

      return context.res
    } catch (err) {
      return this.#handleError(err, c)
    }
  })()
}
```
Sources: [src/hono-base.ts:430-466](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L430-L466)

## Fetch interface and runtime execution

### Standard Fetch Handler and Request Dispatching

The `.fetch()` method serves as the primary entry point for Hono applications, adhering to the standard Web Fetch API signature by accepting a `Request` object, optional environment bindings, and an `ExecutionContext`. It delegates directly to the private `#dispatch()` method using `request.method` and the provided bindings.

```typescript
fetch: (
  request: Request,
  Env?: E['Bindings'] | {},
  executionCtx?: ExecutionContext
) => Response | Promise<Response> = (request, ...rest) => {
  return this.#dispatch(request, rest[1], rest[0], request.method)
}
```
Sources: [src/hono-base.ts:479-485](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L479-L485)

Inside `#dispatch()`, HTTP `HEAD` requests are intercepted and handled specially by delegating to a `GET` dispatch and stripping the response body, returning a new `Response` with a `null` body.

```typescript
if (method === 'HEAD') {
  return (async () =>
    new Response(null, await this.#dispatch(request, executionCtx, env, 'GET')))()
}
```
Sources: [src/hono-base.ts:413-416](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L413-L416)

For all other methods, path resolution is performed via the configured `getPath` helper, and the router matches the method against the path to produce a `matchResult`. A new `Context` instance is then instantiated with the request, matched route handlers, bindings, and execution context.

```typescript
const path = this.getPath(request, { env })
const matchResult = this.router.match(method, path)

const c = new Context(request, {
  path,
  matchResult,
  env,
  executionCtx,
  notFoundHandler: this.#notFoundHandler,
})
```
Sources: [src/hono-base.ts:418-427](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L418-L427)

> [!NOTE]
> If a match yields exactly one handler, Hono bypasses the full `[[Middleware Composition|compose]]` pipeline overhead to execute the handler directly, falling back to the not-found handler if unfinalized.

```typescript
// Do not `compose` if it has only one handler
if (matchResult[0].length === 1) {
  let res: ReturnType<H>
  try {
    res = matchResult[0][0][0][0](c, async () => {
      c.res = await this.#notFoundHandler(c)
    })
  } catch (err) {
    return this.#handleError(err, c)
  }

  return res instanceof Promise
    ? res
        .then(
          (resolved: Response | undefined) =>
            resolved || (c.finalized ? c.res : this.#notFoundHandler(c))
        )
        .catch((err: Error) => this.#handleError(err, c))
    : (res ?? this.#notFoundHandler(c))
}
```
Sources: [src/hono-base.ts:429-448](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L429-L448)

When multiple handlers match, `#dispatch()` invokes `compose` with the matched handlers, error handler, and not-found handler, ensuring that context finalization invariants are strictly enforced on the resulting execution pipeline.

```typescript
  const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler)

  return (async () => {
    try {
      const context = await composed(c)
      if (!context.finalized) {
        throw new Error(
          'Context is not finalized. Did you forget to return a Response object or `await next()`?'
        )
      }

      return context.res
    } catch (err) {
      return this.#handleError(err, c)
    }
  })()
}
```
Sources: [src/hono-base.ts:450-466](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L450-L466)

### Testing Helpers and Service Worker Integration

Hono provides the `.request()` method as a utility primarily designed for testing. It accepts `Request`, string, or `URL` inputs along with optional request initialization options, environment bindings, and execution context, automatically resolving relative paths against a localhost base if necessary.

```typescript
request = (
  input: Request | string | URL,
  requestInit?: RequestInit,
  Env?: E['Bindings'] | {},
  executionCtx?: ExecutionContext
): Response | Promise<Response> => {
  if (input instanceof Request) {
    return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx)
  }
  input = input.toString()
  return this.fetch(
    new Request(
      /^https?:\/\//.test(input) ? input : `http://localhost${mergePath('/', input)}`,
      requestInit
    ),
    Env,
    executionCtx
  )
}
```
Sources: [src/hono-base.ts:499-517](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L499-L517)

For environments adhering to the Service Worker API (such as non-ES module Cloudflare Workers), the `.fire()` method registers a global `fetch` event listener via `addEventListener`, responding to incoming fetch events by invoking `#dispatch()`.

```typescript
fire = (): void => {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  addEventListener('fetch', (event: FetchEventLike): void => {
    event.respondWith(this.#dispatch(event.request, event, undefined, event.request.method))
  })
}
```
Sources: [src/hono-base.ts:536-542](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L536-L542)

> [!WARNING]
> The `.fire()` method is deprecated and should be avoided in modern ES module runtimes; use `fire` from `hono/service-worker` instead.

```typescript
* @deprecated
* Use `fire` from `hono/service-worker` instead.
```
Sources: [src/hono-base.ts:522-523](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts#L522-L523)

## Related

- [Request Lifecycle](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/request-lifecycle)
- [Execution Context](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/execution-context)
- [Router Contracts](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/routing/router-contracts)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/llms.txt) for all pages in this wiki.
