Skip to main content

radix3 vs find-my-way vs trek-router: HTTP Router Engines in Node.js (2026)

·PkgPulse Team

TL;DR

radix3 is the UnJS radix tree router — ultra-fast path matching, used internally by H3 and Nitro, supports static routes, parameters, and wildcards. find-my-way is Fastify's router — radix tree with HTTP method support, parametric routes, constraints (versioning, host-based routing), and the fastest benchmarked router. trek-router is a lightweight trie-based router — simple API, named parameters, used as a building block. In 2026: find-my-way for Fastify ecosystem or standalone use, radix3 for UnJS/H3 ecosystem, trek-router for educational or minimal use cases.

Key Takeaways

  • radix3: ~5M weekly downloads — UnJS, powers H3/Nitro routing, radix tree, TypeScript
  • find-my-way: ~5M weekly downloads — Fastify core, HTTP method support, constraints, fastest
  • trek-router: ~50K weekly downloads — minimal trie router, simple API
  • These are router engines — they match URL paths to handlers, not full frameworks
  • Radix tree routers are O(path length) — much faster than linear array scanning
  • find-my-way is the only one with built-in HTTP method support and constraints

How Router Engines Work

Linear scan (naive):
  routes = ["/api/users", "/api/users/:id", "/api/posts", ...]
  For each request, scan ALL routes until match → O(n) per request

Radix tree (fast):
  /api
    /users
      /:id    → handler
      /        → handler
    /posts
      /:id    → handler
      /        → handler

  Lookup is O(path length), not O(route count)
  1000 routes? Same speed as 10 routes.

radix3

radix3 — UnJS radix tree router:

Basic usage

import { createRouter } from "radix3"

const router = createRouter()

// Static routes:
router.insert("/api/health", { handler: "healthCheck" })
router.insert("/api/packages", { handler: "listPackages" })

// Parameter routes:
router.insert("/api/packages/:name", { handler: "getPackage" })
router.insert("/api/packages/:name/versions/:version", { handler: "getVersion" })

// Wildcard:
router.insert("/static/**", { handler: "serveStatic" })

// Lookup:
const match = router.lookup("/api/packages/react")
// → { handler: "getPackage", params: { name: "react" } }

const match2 = router.lookup("/api/packages/react/versions/19.0.0")
// → { handler: "getVersion", params: { name: "react", version: "19.0.0" } }

const match3 = router.lookup("/static/images/logo.png")
// → { handler: "serveStatic", params: { _: "images/logo.png" } }

const miss = router.lookup("/not/found")
// → null

Remove and update routes

import { createRouter } from "radix3"

const router = createRouter()

router.insert("/api/packages", { handler: "v1" })

// Remove:
router.remove("/api/packages")

// Re-insert with updated data:
router.insert("/api/packages", { handler: "v2" })

How H3 uses radix3

// H3 (UnJS HTTP framework) uses radix3 internally:
import { createApp, createRouter, eventHandler } from "h3"

const app = createApp()
const router = createRouter()

// These routes are stored in a radix3 tree:
router.get("/api/packages", eventHandler(() => ({ packages: [] })))
router.get("/api/packages/:name", eventHandler((event) => {
  return { name: event.context.params.name }
}))

app.use(router)
// radix3 handles the path matching — O(path length) lookups

find-my-way

find-my-way — Fastify's router:

Basic usage

import FindMyWay from "find-my-way"

const router = FindMyWay()

// Routes with HTTP methods:
router.on("GET", "/api/packages", (req, res, params) => {
  res.end(JSON.stringify({ packages: [] }))
})

router.on("GET", "/api/packages/:name", (req, res, params) => {
  res.end(JSON.stringify({ name: params.name }))
})

router.on("POST", "/api/packages", (req, res, params) => {
  res.end(JSON.stringify({ created: true }))
})

// Wildcard:
router.on("GET", "/static/*", (req, res, params) => {
  res.end(`Serving: ${params["*"]}`)
})

// Lookup:
router.lookup(req, res)  // Finds and calls the matching handler

Parametric routes

const router = FindMyWay()

// Named parameters:
router.on("GET", "/api/packages/:name", handler)
// Matches: /api/packages/react → params.name = "react"

// Multiple parameters:
router.on("GET", "/api/packages/:name/versions/:version", handler)
// Matches: /api/packages/react/versions/19.0.0

// Regex constraints on parameters:
router.on("GET", "/api/packages/:id(^\\d+$)", handler)
// Matches: /api/packages/123 (only numbers)
// Rejects: /api/packages/react

// Multi-parametric:
router.on("GET", "/api/:org-:repo", handler)
// Matches: /api/facebook-react → params = { org: "facebook", repo: "react" }

Constraints (versioning, host-based)

import FindMyWay from "find-my-way"

const router = FindMyWay()

// Version-based routing:
router.on("GET", "/api/packages", { constraints: { version: "1.0.0" } }, (req, res) => {
  res.end("API v1")
})

router.on("GET", "/api/packages", { constraints: { version: "2.0.0" } }, (req, res) => {
  res.end("API v2")
})

// Host-based routing:
router.on("GET", "/", { constraints: { host: "api.pkgpulse.com" } }, (req, res) => {
  res.end("API subdomain")
})

router.on("GET", "/", { constraints: { host: "www.pkgpulse.com" } }, (req, res) => {
  res.end("Web subdomain")
})

Performance

find-my-way benchmarks (operations per second):

  Static routes:     ~15M ops/sec
  Parametric routes: ~5M ops/sec
  Wildcard routes:   ~8M ops/sec

  vs koa-router:     ~500K ops/sec (30x slower)
  vs express router:  ~300K ops/sec (50x slower)

  find-my-way is consistently the fastest Node.js router.

How Fastify uses find-my-way

// Fastify uses find-my-way internally for ALL route matching:
import Fastify from "fastify"

const app = Fastify()

// These routes are stored in find-my-way's radix tree:
app.get("/api/packages/:name", async (request) => {
  return { name: request.params.name }
})

// Fastify's constraint system (versioning) is powered by find-my-way:
app.route({
  method: "GET",
  url: "/api/data",
  constraints: { version: "2.0.0" },
  handler: async () => ({ version: 2 }),
})

trek-router

trek-router — minimal trie router:

Basic usage

import Router from "trek-router"

const router = new Router()

// Add routes:
router.add("GET", "/api/packages", "listPackages")
router.add("GET", "/api/packages/:name", "getPackage")
router.add("POST", "/api/packages", "createPackage")

// Find:
const [handler, params] = router.find("GET", "/api/packages/react")
// handler → "getPackage"
// params → [{ name: "name", value: "react" }]

const [handler2, params2] = router.find("GET", "/not-found")
// handler2 → undefined
// params2 → undefined

trek-router limitations

trek-router:
  ✅ Simple API (add + find)
  ✅ Trie-based (fast lookups)
  ✅ Lightweight (~2 KB)

  ❌ No wildcard routes
  ❌ No constraints
  ❌ No route removal
  ❌ Not actively maintained
  ❌ Limited parameter syntax

For production: use find-my-way or radix3

Feature Comparison

Featureradix3find-my-waytrek-router
Data structureRadix treeRadix treeTrie
HTTP methods❌ (path only)
Named parameters
Wildcard routes
Regex constraints
Version routing
Host routing
Route removal
TypeScript⚠️
Used byH3, NitroFastify
Weekly downloads~5M~5M~50K

When to Use Each

Use radix3 if:

  • Building with H3/Nitro (UnJS ecosystem)
  • Need a pure path matcher (no HTTP method binding)
  • Want the simplest possible radix tree router
  • Building custom routing logic on top

Use find-my-way if:

  • Building with Fastify or standalone HTTP servers
  • Need HTTP method support (GET/POST/PUT/DELETE)
  • Want constraint-based routing (versioning, host)
  • Need the fastest benchmarked router

Use trek-router if:

  • Learning how trie/radix routers work
  • Need the most minimal router possible
  • Building a simple proof of concept

Methodology

Download data from npm registry (weekly average, February 2026). Feature comparison based on radix3 v1.x, find-my-way v8.x, and trek-router v1.x.

Compare HTTP routing and server frameworks on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.