---
title: "RegExp Router"
description: "The RegExpRouter implements the unified Router Contracts interface by compiling registered path structures and HTTP method sets into a single optimized regular expression src/router/reg-exp-router/..."
last_updated: "2026-10-12T08:14:12.747835+00:00"
canonical_url: "https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/routing/regexp-router"
---

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

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

- [src/router/reg-exp-router/node.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts)
- [src/router/reg-exp-router/trie.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts)
- [src/router/reg-exp-router/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts)
- [src/router/reg-exp-router/prepared-router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts)
- [src/helper/route/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/route/index.ts)
- [src/types.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/types.ts)
- [src/router/reg-exp-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/index.ts)
- [src/hono-base.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono-base.ts)
- [src/router/linear-router/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/linear-router/router.ts)
- [src/router/trie-router/node.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/trie-router/node.ts)
- [src/router/reg-exp-router/matcher.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts)
- [src/preset/quick.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/preset/quick.ts)
- [src/router/trie-router/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/trie-router/router.ts)
- [src/helper/ssg/ssg.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/ssg/ssg.ts)
- [src/request.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/request.ts)
- [src/hono.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/hono.ts)
- [src/utils/url.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/url.ts)
- [src/router/trie-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/trie-router/index.ts)
- [src/router/pattern-router/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/pattern-router/router.ts)
- [src/router/pattern-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/pattern-router/index.ts)
- [src/helper/dev/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/helper/dev/index.ts)
- [src/preset/tiny.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/preset/tiny.ts)
- [src/router/linear-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/linear-router/index.ts)
- [src/context.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/context.ts)
- [src/router/smart-router/index.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/smart-router/index.ts)
- [src/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router.ts)
- [package.json](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/package.json)
- [src/router/smart-router/router.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/smart-router/router.ts)
- [src/utils/compress.ts](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/utils/compress.ts)
</details>

## Overview

The `RegExpRouter` implements the unified [Router Contracts](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/routing/router-contracts) interface by compiling registered path structures and HTTP method sets into a single optimized regular expression [src/router/reg-exp-router/router.ts:122-207](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L122-L207). Operating as a core backend option for [Application Core](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/core-engine/application-core) via SmartRouter, it transforms wildcard patterns, path parameters, and static segments into a radix-style node trie that generates a unified matcher [src/router/reg-exp-router/router.ts:34-103](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L34-L103), [src/router/reg-exp-router/trie.ts:6-73](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L6-L73). By consolidating route evaluation, it avoids linear array scans during request dispatching [src/router/reg-exp-router/matcher.ts:10-33](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L10-L33). Additionally, `PreparedRegExpRouter` supports ahead-of-time serialization and pre-compilation patterns to eliminate runtime startup overhead [src/router/reg-exp-router/prepared-router.ts:9-164](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L9-L164).

Sources: [src/router/reg-exp-router/router.ts:34-207](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L34-L207), [src/router/reg-exp-router/trie.ts:6-73](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L6-L73), [src/router/reg-exp-router/matcher.ts:10-33](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L10-L33), [src/router/reg-exp-router/prepared-router.ts:9-164](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L9-L164)

## Architecture and Routing Model

### Public Router Implementation and State Management

The `RegExpRouter` class implements the [Router Contracts](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/routing/router-contracts) interface, managing route registration and request matching state through private collections [src/router/reg-exp-router/router.ts:122-130](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L122-L130). During the initialization phase, the router constructs separate storage maps for middleware and terminal routes under the `ALL` HTTP method (METHOD_NAME_ALL) [src/router/reg-exp-router/router.ts:127-130](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L127-L130). 

```typescript
export class RegExpRouter<T> implements Router<T> {
  name: string = 'RegExpRouter'
  #middleware?: Record<string, Record<string, HandlerWithMetadata<T>[]>>
  #routes?: Record<string, Record<string, HandlerWithMetadata<T>[]>>

  constructor() {
    this.#middleware = { [METHOD_NAME_ALL]: Object.create(null) }
    this.#routes = { [METHOD_NAME_ALL]: Object.create(null) }
  }
  // ...
}
```
Sources: [src/router/reg-exp-router/router.ts:122-130](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L122-L130)

> [!IMPORTANT]
> Once a match operation compiles the underlying regular expression, the `#middleware` and `#routes` internal maps become `undefined`. Any subsequent call to `add()` after building will throw an error with `MESSAGE_MATCHER_IS_ALREADY_BUILT`.

Sources: [src/router/reg-exp-router/router.ts:136-138](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L136-L138)

### Route Registration Lifecycle

The `add()` method populates internal HTTP method tables, handles optional parameters via `checkOptionalParameter`, and processes wildcard suffix patterns [src/router/reg-exp-router/router.ts:132-204](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L132-L204). When a new HTTP method is encountered for the first time, the router initializes its maps by copying existing entries registered under `ALL` [src/router/reg-exp-router/router.ts:140-147](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L140-L147).

| Internal Property / Parameter | Type | Purpose |
| :--- | :--- | :--- |
| `name` | `string` | Identifies the router backend as `'RegExpRouter'`. |
| `#middleware` | `Record<string, Record<string, HandlerWithMetadata<T>[]>>` | Stores registered middleware paths grouped by HTTP method. |
| `#routes` | `Record<string, Record<string, HandlerWithMetadata<T>[]>>` | Stores terminal route handlers grouped by HTTP method. |
| `METHOD_NAME_ALL` | `string` | Constant designating catch-all HTTP methods (`'ALL'`). |
| `nullMatcher` | `Matcher<any>` | Fallback matcher for empty routing tables (`[/^$/, [], Object.create(null)]`). |

Sources: [src/router.ts:9](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router.ts#L9), [src/router/reg-exp-router/router.ts:17](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L17), [src/router/reg-exp-router/router.ts:122-130](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L122-L130), [src/router/reg-exp-router/router.ts:140-147](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L140-L147)

Wildcard patterns ending in `*` are expanded and tested against existing route entries using cached regular expressions built via `buildWildcardRegExp` [src/router/reg-exp-router/router.ts:20-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L20-L28), [src/router/reg-exp-router/router.ts:155-186](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L155-L186). 

```typescript
function buildWildcardRegExp(path: string): RegExp {
  return (wildcardRegExpCache[path] ??= new RegExp(
    path === '*'
      ? ''
      : `^${path.replace(/\/\*$|([.\\+*[^\]$()])/g, (_, metaChar) =>
          metaChar ? `\\${metaChar}` : '(?:|/.*)'
        )}$`
  ))
}
```
Sources: [src/router/reg-exp-router/router.ts:20-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L20-L28)

### Matcher Compilation Workflow

When request matching occurs, `buildMatcherFromPreprocessedRoutes` sorts paths, separates static and dynamic segments, and delegates structural insertion to a `Trie` instance [src/router/reg-exp-router/router.ts:34-82](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L34-L82). Static paths are stored directly in a `staticMap` for O(1) lookups, while dynamic paths contribute to a consolidated regular expression [src/router/reg-exp-router/router.ts:43-82](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L43-L82).

```typescript
function buildMatcherFromPreprocessedRoutes<T>(
  routes: [string, HandlerWithMetadata<T>[]][]
): Matcher<T> {
  const trie = new Trie()
  const handlerData: HandlerData<T>[] = []
  if (routes.length === 0) {
    return nullMatcher
  }
```
Sources: [src/router/reg-exp-router/router.ts:34-42](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L34-L42)

> [!WARNING]
> Path insertions that conflict with structural constraints or invalid syntax throw a `PATH_ERROR`, which the router catches and re-throws as an `UnsupportedPathError`.

Sources: [src/router/reg-exp-router/router.ts:61-65](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L61-L65)

## Trie Intermediate Representation

### Path Tokenization and Group Preservation

The `Trie` class manages routing structures through a root `Node` instance and a shared context tracking variable indices (`varIndex`) [src/router/reg-exp-router/trie.ts:6-8](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L6-L8). When a route path is inserted, the `insert()` method first isolates bracketed custom regular expression groups (such as `:id{[0-9]+}`) by replacing them with temporary placeholder markers (`@\i`) [src/router/reg-exp-router/trie.ts:13-26](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L13-L26). It then tokenizes the modified path using a regular expression that matches named parameters (`:[^\/]+`), wildcard suffixes (`/\*$`), or individual characters, before restoring the original bracketed expressions into the correct token positions [src/router/reg-exp-router/trie.ts:33-42](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L33-L42).

```typescript
    const groups: [string, string][] = [] // [mark, original string]
    for (let i = 0; ; ) {
      let replaced = false
      path = path.replace(/\{[^}]+\}/g, (m) => {
        const mark = `@\\${i}`
        groups[i] = [mark, m]
        i++
        replaced = true
        return mark
      })
      if (!replaced) {
        break
      }
    }
```
Sources: [src/router/reg-exp-router/trie.ts:13-26](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L13-L26)

### Node Graph Insertion and Conflict Checks

The `Node` class recursively inserts token arrays into its `#children` record map [src/router/reg-exp-router/node.ts:45-133](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L45-L133). During insertion, tokens are evaluated to determine their pattern types—distinguishing between standalone wildcards (`*`), tail wildcards (`/*`), named parameters with custom expressions, common label patterns (`[^/]+`), and literal characters [src/router/reg-exp-router/node.ts:69-78](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L69-L78).

| Token / Pattern Type | Regular Expression String | Assigned Variable / Purpose |
| :--- | :--- | :--- |
| `*` (terminal) | `.*` (`ONLY_WILDCARD_REG_EXP_STR`) | Matches all trailing paths without capturing a parameter name. |
| `*` (non-terminal) | `[^/]+` (`LABEL_REG_EXP_STR`) | Acts as a generic path segment label. |
| `/*` | `(?:|/.*)` (`TAIL_WILDCARD_REG_EXP_STR`) | Matches an optional trailing slash followed by any path content. |
| `:label` | `[^/]+` (`LABEL_REG_EXP_STR`) | Captures standard path segments into `paramMap`. |
| `:label{expr}` | `expr` (custom regex string) | Validates and captures custom regex patterns; enforces non-capturing group constraints. |

Sources: [src/router/reg-exp-router/node.ts:1-3](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L1-L3), [src/router/reg-exp-router/node.ts:69-78](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L69-L78)

> [!CAUTION]
> Inserting conflicting segment types into the same Trie node level—such as mixing literal characters with multi-character wildcards or mixing distinct regular expression patterns—immediately throws `PATH_ERROR`. Custom expressions containing capturing groups without non-capturing prefixes (`(?!:\?)`) are also rejected.

Sources: [src/router/reg-exp-router/node.ts:87-91](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L87-L91), [src/router/reg-exp-router/node.ts:96-102](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L96-L102), [src/router/reg-exp-router/node.ts:117-124](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L117-L124)

## Pattern Compilation and Radix Merging

### Recursive Regex String Synthesis and Child Key Sorting

The `Node` class converts the hierarchical trie structure into a unified regular expression string via the `buildRegExpStr()` method [src/router/reg-exp-router/node.ts:135-161](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L135-L161). Child node keys are retrieved, sorted using the `compareKey` function, and mapped into sub-expressions [src/router/reg-exp-router/node.ts:136-147](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L136-L147). 

```typescript
function compareKey(a: string, b: string): number {
  if (a.length === 1) {
    return b.length === 1 ? (a < b ? -1 : 1) : -1
  }
  if (b.length === 1) {
    return 1
  }

  // wildcard
  if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
    return 1
  } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
    return -1
  }

  // label
  if (a === LABEL_REG_EXP_STR) {
    return 1
  } else if (b === LABEL_REG_EXP_STR) {
    return -1
  }

  return a.length === b.length ? (a < b ? -1 : 1) : b.length - a.length
}
```
Sources: [src/router/reg-exp-router/node.ts:20-43](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L20-L43)

The sorting algorithm establishes strict evaluation precedence by organizing node keys in the following order: literal strings, special custom patterns, common label patterns (`[^/]+`), and wildcards (`.*` or `(?:|/.*)`) [src/router/reg-exp-router/node.ts:14-19](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L14-L19).

```typescript
buildRegExpStr(): string {
  const childKeys = Object.keys(this.#children).sort(compareKey)

  const strList = childKeys.map((k) => {
    const c = this.#children[k]
    return (
      (typeof c.#varIndex === 'number'
        ? `(${k})@${c.#varIndex}`
        : regExpMetaChars.has(k)
          ? `\\${k}`
          : k) + c.buildRegExpStr()
    )
  })

  if (typeof this.#index === 'number') {
    strList.unshift(`#${this.#index}`)
  }

  if (strList.length === 0) {
    return ''
  }
  if (strList.length === 1) {
    return strList[0]
  }

  return '(?:' + strList.join('|') + ')'
}
```
Sources: [src/router/reg-exp-router/node.ts:135-161](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L135-L161)

> [!NOTE]
> When multiple child nodes exist at the same level, `buildRegExpStr()` joins them with alternation operators (`|`) wrapped in non-capturing groups `(?:...)`. If a node marks a route termination point (`#index`), its handler identifier is prepended to the string list before alternation synthesis [src/router/reg-exp-router/node.ts:149-160](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L149-L160).

Sources: [src/router/reg-exp-router/node.ts:149-160](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/node.ts#L149-L160)

### Wildcard Normalization and Root Compilation

Wildcard routes and trailing patterns undergo specific normalization before and during trie integration [src/router/reg-exp-router/router.ts:20-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L20-L28). The router maintains a `wildcardRegExpCache` to compile wildcard expressions, translating `/*` suffixes into safe literal escape sequences combined with `(?:|/.*)` optional path matchers [src/router/reg-exp-router/router.ts:19-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L19-L28).

```typescript
let wildcardRegExpCache: Record<string, RegExp> = Object.create(null)
function buildWildcardRegExp(path: string): RegExp {
  return (wildcardRegExpCache[path] ??= new RegExp(
    path === '*'
      ? ''
      : `^${path.replace(/\/\*$|([.\\+*[^\]$()])/g, (_, metaChar) =>
          metaChar ? `\\${metaChar}` : '(?:|/.*)'
        )}$`
  ))
}
```
Sources: [src/router/reg-exp-router/router.ts:19-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/router.ts#L19-L28)

Once the trie graph is fully populated, `Trie.buildRegExp()` finalizes the compiled string by processing handler markers (`#\d+`), parameter markers (`@\d+`), and wildcard endings (`\.\*\$`) [src/router/reg-exp-router/trie.ts:49-73](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L49-L73). It constructs `indexReplacementMap` and `paramReplacementMap` lookup structures to align capturing groups with handler indices, returning a fully operational `RegExp` instance anchored with a caret (`^`) [src/router/reg-exp-router/trie.ts:55-73](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L55-L73).

```typescript
buildRegExp(): [RegExp, ReplacementMap, ReplacementMap] {
  let regexp = this.#root.buildRegExpStr()
  if (regexp === '') {
    return [/^$/, [], []] // never match
  }

  let captureIndex = 0
  const indexReplacementMap: ReplacementMap = []
  const paramReplacementMap: ReplacementMap = []

  regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => {
    if (handlerIndex !== undefined) {
      indexReplacementMap[++captureIndex] = Number(handlerIndex)
      return '$()'
    }
    if (paramIndex !== undefined) {
      paramReplacementMap[Number(paramIndex)] = ++captureIndex
      return ''
    }

    return ''
  })

  return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap]
}
```
Sources: [src/router/reg-exp-router/trie.ts:49-73](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/trie.ts#L49-L73)

## Matcher Execution and Param Extraction

### Single-Pass Regex Matching and Static Map Lookups

When a request path is evaluated, the `match` function first resolves the appropriate `Matcher<T>` tuple for the HTTP method by checking method-specific matchers or falling back to `METHOD_NAME_ALL` [src/router/reg-exp-router/matcher.ts:10-15](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L10-L15). 

```typescript
export function match<R extends Router<T>, T>(this: R, method: string, path: string): Result<T> {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const matchers: MatcherMap<T> = (this as any).buildAllMatchers()

  const match = ((method, path) => {
    const matcher = (matchers[method] || matchers[METHOD_NAME_ALL]) as Matcher<T>

    const staticMatch = matcher[2][path]
    if (staticMatch) {
      return staticMatch
    }
```
Sources: [src/router/reg-exp-router/matcher.ts:10-20](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L10-L20)

Before executing regular expression evaluation, the router checks the `StaticMap` (`matcher[2]`) for an exact path match [src/router/reg-exp-router/matcher.ts:17-20](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L17-L20). If a static entry exists, it returns the precompiled handler list and empty parameters directly, avoiding regex overhead entirely [src/router/reg-exp-router/matcher.ts:17-20](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L17-L20). When the path is dynamic, `path.match(matcher[0])` executes a single-pass regular expression match across the compiled routing tree [src/router/reg-exp-router/matcher.ts:22-25](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L22-L25).

```typescript
  const match = path.match(matcher[0])
  if (!match) {
    return [[], emptyParam]
  }

  const index = match.indexOf('', 1)
  return [matcher[1][index], match]
}) as Router<T>['match']
```
Sources: [src/router/reg-exp-router/matcher.ts:21-29](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L21-L29)

> [!NOTE]
> If the regular expression test fails (`!match`), the matcher immediately returns an empty handler list and an empty parameter array `[[], emptyParam]` [src/router/reg-exp-router/matcher.ts:23-25](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L23-L25).

Sources: [src/router/reg-exp-router/matcher.ts:23-25](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L23-L25)

### Index-to-Handler Dispatch and Capture-Group Extraction

Once a successful regex match occurs, the router determines which route branch matched by scanning the match array for the handler index marker [src/router/reg-exp-router/matcher.ts:27-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L27-L28). The expression `match.indexOf('', 1)` finds the empty string boundary or group index corresponding to the active route branch [src/router/reg-exp-router/matcher.ts:27](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L27).

```typescript
const index = match.indexOf('', 1)
return [matcher[1][index], match]
```
Sources: [src/router/reg-exp-router/matcher.ts:27-28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L27-L28)

The resulting index maps directly to `matcher[1]`, which holds the handler array and pre-mapped parameter keys for that specific route branch [src/router/reg-exp-router/matcher.ts:28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L28). The matched RegExp result array itself is returned alongside the handlers, supplying captured parameter values extracted from dynamic segments [src/router/reg-exp-router/matcher.ts:28](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L28).

> [!TIP]
> After the initial match call executes, `this.match` is dynamically rewritten on the router instance to point directly to the inner closure function, bypassing method lookup overhead on all subsequent requests [src/router/reg-exp-router/matcher.ts:14-32](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L14-L32).

Sources: [src/router/reg-exp-router/matcher.ts:14-32](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/matcher.ts#L14-L32)

## Ahead-of-Time Prepared Matching

### Serialization and Pre-compilation Patterns

To bypass dynamic route compilation overhead during server startup, `PreparedRegExpRouter` enables ahead-of-time initialization [src/router/reg-exp-router/prepared-router.ts:9-17](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L9-L17). The helper function `buildInitParams` provisions a temporary `RegExpRouter` instance, registers all target paths against `METHOD_NAME_ALL`, and exports the resulting matcher structures [src/router/reg-exp-router/prepared-router.ts:95-154](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L95-L154). It constructs a `relocateMap` that maps individual paths to their corresponding handler indices and parameter index maps [src/router/reg-exp-router/prepared-router.ts:111-144](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L111-L144).

```typescript
export const buildInitParams: (params: {
  paths: string[]
}) => ConstructorParameters<typeof PreparedRegExpRouter> = ({ paths }) => {
  const RegExpRouterWithMatcherExport = class<T> extends RegExpRouter<T> {
    buildAndExportAllMatchers() {
      return this.buildAllMatchers()
    }
  }
  const router = new RegExpRouterWithMatcherExport<string>()
  for (const path of paths) {
    router.add(METHOD_NAME_ALL, path, path)
  }

  const matchers = router.buildAndExportAllMatchers()
  const all = matchers[METHOD_NAME_ALL] as Matcher<string>

  const relocateMap: RelocateMap = {}
  for (const path of paths) {
    if (path === '/*' || path === '*') {
      continue
    }
    all[1].forEach((list, i) => {
      list.forEach(([p, map]) => {
        if (p === path) {
          if (relocateMap[path]) {
            relocateMap[path][0][1] = {
              ...relocateMap[path][0][1],
              ...map,
            }
          } else {
            relocateMap[path] = [[[], map]]
          }
          if (relocateMap[path][0][0].findIndex((j) => j === i) === -1) {
            relocateMap[path][0][0].push(i)
          }
        }
      })
    })
    for (const path2 in all[2]) {
      all[2][path2][0].forEach(([p]) => {
        if (p === path) {
          relocateMap[path] ||= [[[]]]
          const value = path2 === path ? '' : path2
          if (relocateMap[path][0][0].findIndex((v) => v === value) === -1) {
            relocateMap[path][0][0].push(value)
          }
        }
      })
    }
  }

  for (let i = 0, len = all[1].length; i < len; i++) {
    all[1][i] = all[1][i] ? [] : (0 as unknown as HandlerData<string>)
  }
  for (const path in all[2]) {
    all[2][path][0] = []
  }

  return [matchers, relocateMap]
}
```
Sources: [src/router/reg-exp-router/prepared-router.ts:95-154](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L95-L154)

The companion utility `serializeInitParams` translates these compilation artifacts into stringified JSON formats [src/router/reg-exp-router/prepared-router.ts:156-165](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L156-L165). To preserve native regular expression objects across serialization boundaries, it converts `RegExp` instances into tagged string placeholders (`##...##`) and restores them via regex replacement during output generation [src/router/reg-exp-router/prepared-router.ts:160-164](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L160-L164).

```typescript
export const serializeInitParams: (
  params: ConstructorParameters<typeof PreparedRegExpRouter>
) => string = ([matchers, relocateMap]) => {
  // Embed the regular expression as a result of `toString()` so that it can be evaluated as JavaScript.
  const matchersStr = JSON.stringify(matchers, (_, value) =>
    value instanceof RegExp ? `##${value.toString()}##` : value
  ).replace(/"##(.+?)##"/g, (_, str) => str.replace(/\\\\/g, '\\'))
  const relocateMapStr = JSON.stringify(relocateMap)
  return `[${matchersStr},${relocateMapStr}]`
}
```
Sources: [src/router/reg-exp-router/prepared-router.ts:156-165](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L156-L165)

### Runtime Mechanics and Relocation Maps

At runtime, `PreparedRegExpRouter` accepts precompiled `MatcherMap` and `RelocateMap` collections directly through its constructor [src/router/reg-exp-router/prepared-router.ts:14-17](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L14-L17). When additional routes are added dynamically via `add`, the router references the `relocateMap` to insert handlers into pre-allocated matcher slots without rebuilding the trie or re-synthesizing regular expression patterns [src/router/reg-exp-router/prepared-router.ts:47-86](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L47-L86).

```typescript
add(method: string, path: string, handler: T) {
  if (!this.#matchers[method]) {
    const all = this.#matchers[METHOD_NAME_ALL] as Matcher<T>
    const staticMap = {} as StaticMap<T>
    for (const key in all[2]) {
      staticMap[key] = [all[2][key][0].slice(), emptyParam] as Result<T>
    }
    this.#matchers[method] = [
      all[0],
      all[1].map((list) => (Array.isArray(list) ? list.slice() : 0)) as HandlerData<T>[],
      staticMap,
    ]
  }

  if (path === '/*' || path === '*') {
    const handlerData: [T, ParamIndexMap] = [handler, {}]
    if (method === METHOD_NAME_ALL) {
      for (const m in this.#matchers) {
        this.#addWildcard(m, handlerData)
      }
    } else {
      this.#addWildcard(method, handlerData)
    }
    return
  }

  const data = this.#relocateMap[path]
  if (!data) {
    throw new Error(`Path ${path} is not registered`)
  }
  for (const [indexes, map] of data) {
    if (method === METHOD_NAME_ALL) {
      for (const m in this.#matchers) {
        this.#addPath(m, path, handler, indexes, map)
      }
    } else {
      this.#addPath(method, path, handler, indexes, map)
    }
  }
}
```
Sources: [src/router/reg-exp-router/prepared-router.ts:47-86](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L47-L86)

> [!WARNING]
> If a route path is registered via `add` that was not included in the initial path array passed to `buildInitParams`, the router throws an error stating that the path is not registered [src/router/reg-exp-router/prepared-router.ts:73-76](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L73-L76).

Sources: [src/router/reg-exp-router/prepared-router.ts:73-76](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L73-L76)

| Parameter / Property | Type | Description |
| :--- | :--- | :--- |
| `#matchers` | `MatcherMap<T>` | Method-indexed collection containing compiled regular expressions, handler lists, and static maps [src/router/reg-exp-router/prepared-router.ts:9-17](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L9-L17) |
| `#relocateMap` | `RelocateMap` | Mapping of paths to route index arrays and parameter index configurations [src/router/reg-exp-router/prepared-router.ts:7-12](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L7-L12) |
| `buildInitParams` | `Function` | Pre-builds matchers and computes relocation targets from a set of paths [src/router/reg-exp-router/prepared-router.ts:95-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L95-L97) |
| `serializeInitParams` | `Function` | Serializes matchers and relocation maps into an evaluable string representation [src/router/reg-exp-router/prepared-router.ts:156-158](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L156-L158) |

Sources: [src/router/reg-exp-router/prepared-router.ts:7-17](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L7-L17), [src/router/reg-exp-router/prepared-router.ts:95-97](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L95-L97), [src/router/reg-exp-router/prepared-router.ts:156-158](https://github.com/blade47/hono/blob/e50df01453e71b071c3e6136b161b160b9fdf916/src/router/reg-exp-router/prepared-router.ts#L156-L158)

## Related

- [Router Contracts](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/routing/router-contracts)
- [Trie and Smart Routers](https://www.doc0.dev/docs/552ca36e-f67e-41c3-a07a-def9bd9551b0/technical/routing/trie-and-smart-routers)


## Sitemap

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