New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

route-core

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

route-core

Ultra‑fast, minimal routing engine for Node.js. Built for hot paths.

Source
npmnpm
Version
0.0.2
Version published
Weekly downloads
32
-23.81%
Maintainers
1
Weekly downloads
 
Created
Source

route-core

route-core is a small, zero-runtime-dependency routing engine for Node.js frameworks. It maps HTTP methods and paths to numeric storeId values, returns decoded route parameters, preserves the registered route template, and leaves handler ownership to the host framework.

It is designed for framework adapters that want a focused router core instead of a full HTTP dispatcher.

  • Small public API: add, find, lookup, and allowed.
  • Framework-owned stores: route-core stores ids and params only; your framework owns handlers, middleware, and metadata.
  • Template-aware matching: matches include routePath, which is useful for low-cardinality values such as req.route.
  • CJS, ESM, and TypeScript support: the package exports CommonJS, ES module, and declaration entry points.

Contents

Install

npm install route-core

Quick Start

CommonJS:

const { createRouter } = require('route-core')

ES modules:

import { createRouter } from 'route-core'

Basic usage:

const { createRouter } = require('route-core')

const router = createRouter()

router.add('GET', '/users', 0)
router.add('GET', '/users/:id', 1)
router.add('POST', '/users', 2)

console.log(router.find('GET', '/users'))
// { storeId: 0, params: null, routePath: '/users' }

console.log(router.find('GET', '/users/42'))
// { storeId: 1, params: { id: '42' }, routePath: '/users/:id' }

console.log(router.allowed('/users'))
// ['GET', 'POST']

Use lookup() when your adapter wants a direct callback on match:

router.lookup('GET', '/users/42', (storeId, params, routePath) => {
  console.log(storeId)   // 1
  console.log(params)    // { id: '42' }
  console.log(routePath) // '/users/:id'
})

API Reference

createRouter(options?)

Creates and returns a new Router instance.

function createRouter(options?: RouterOptions): Router

RouterOptions

OptionTypeDefaultDescription
ignoreTrailingSlashbooleantrueTreats /foo and /foo/ as the same route
caseSensitivebooleanfalseWhen false, the matcher normalizes path keys to lowercase
maxParamLengthnumber500Maximum decoded length for a parameter segment; overflow returns null
allowWildcardbooleantrueWhen false, wildcard routes throw InvalidPathError

router.add(method, path, storeId)

Registers a route.

router.add(method: string, path: string, storeId: number): void
ParameterDescription
methodHTTP method. Built-ins include GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, CONNECT, and ANY. Methods are normalized to uppercase.
pathRoute pattern. Supports static segments, :param named parameters, and trailing *name wildcards.
storeIdNon-negative safe integer returned on match. Your framework can map it to handlers or stores.

Errors:

Error classCodeTrigger
RouteConflictErrorERR_ROUTE_CONFLICTDuplicate registration for the same method and normalized route shape
InvalidPathErrorERR_INVALID_PATHWildcard route used while allowWildcard is false
InvalidMethodErrorERR_INVALID_METHODEmpty method string
InvalidStoreIdErrorERR_INVALID_STORE_IDstoreId is not a non-negative safe integer

router.find(method, path)

Returns MatchResult on hit, or null on miss or invalid parameter input.

router.find(method: string, path: string): MatchResult | null
interface MatchResult {
  storeId: number
  params: Record<string, string> | null
  routePath: string
}

find() returns null when:

  • No route matches.
  • The matched parameter value exceeds maxParamLength.
  • URL decoding fails for a matched parameter or wildcard value.

router.lookup(method, path, onMatch)

Looks up a route and calls the callback directly on hit. This is intended for adapter hot paths.

router.lookup(
  method: string,
  path: string,
  onMatch: (storeId: number, params: Record<string, string> | null, routePath: string) => void,
): boolean
Return valueMeaning
trueA route matched and onMatch was called
falseNo route matched, or matched params were invalid

lookup() follows the same matching semantics as find(), but avoids returning a MatchResult object.

router.allowed(path)

Returns the registered methods for a path so you can distinguish 404 Not Found from 405 Method Not Allowed.

router.allowed(path: string): string[] | null
Return valueMeaning
nullNo path match exists in any method bucket
string[]The path exists, but the current request method is not registered

Call allowed() only after find() returns null.

404 vs 405

const { createRouter } = require('route-core')

const router = createRouter()
router.add('GET', '/users', 0)
router.add('POST', '/users', 1)

function dispatch(method, pathname, res) {
  const match = router.find(method, pathname)
  if (match) {
    return match
  }

  const methods = router.allowed(pathname)
  if (methods === null) {
    res.writeHead(404)
  } else {
    res.writeHead(405, { Allow: methods.join(', ') })
  }
}

Route Syntax

URL and Parameter Normalization

route-core splits on the raw / delimiter before decoding parameter values, so %2F stays inside the matched segment.

Input handlingBehavior
query/hashIgnored before matching
percent decodingApplied only after a route is matched
%2FPreserved inside the segment and decoded to / in params
caseSensitive=falseAffects matching keys only; returned params keep request casing
maxParamLengthChecked against the decoded parameter or wildcard value
Pattern typeExampleMatch behavior
Static/users/profileExact match
Param/users/:id/users/42 -> { id: '42' }
Wildcard/assets/*file/assets/js/app.js -> { file: 'js/app.js' }
Bare wildcard*Matches any path

Normalized route shapes are unique. For example, /users/:id conflicts with /users/:name, and /assets/*file conflicts with /assets/*path.

Priority

When multiple patterns can match the same request path, route-core uses this order:

static > :param > *wildcard

ANY Method

ANY acts as a fallback bucket. The router checks the concrete method first, then ANY.

router.add('GET', '/health', 0)
router.add('ANY', '/health', 1)

router.find('GET', '/health')
// { storeId: 0, params: null, routePath: '/health' }

router.find('DELETE', '/health')
// { storeId: 1, params: null, routePath: '/health' }

Framework Integration

route-core is transport-agnostic. A common integration pattern is storeId -> store mapping:

import { createRouter } from 'route-core'

interface RouteStore {
  handler: (req: any, res: any) => void
  middleware: Function[]
}

const router = createRouter({ ignoreTrailingSlash: true })
const storeMap = new Map<number, RouteStore>()
let nextId = 0

function register(method: string, path: string, store: RouteStore) {
  const id = nextId++
  router.add(method, path, id)
  storeMap.set(id, store)
}

function resolve(method: string, pathname: string, res: any) {
  const match = router.find(method, pathname)
  if (match) {
    return {
      store: storeMap.get(match.storeId)!,
      params: match.params ?? {},
      route: match.routePath,
    }
  }

  const methods = router.allowed(pathname)
  if (methods) {
    res.writeHead(405, { Allow: methods.join(', ') })
  } else {
    res.writeHead(404)
  }
  return null
}

TypeScript

All public types are exported from the package root:

import { createRouter } from 'route-core'
import type { LookupHandler, MatchResult, Router, RouterOptions } from 'route-core'

const router: Router = createRouter({ caseSensitive: true })

Language-Specific Documentation

Documentation linkage:

  • This root README.md is the default English package entry for npm and GitHub.
  • docs/README.zh-CN.md is the Chinese companion guide for the same public API and usage model.
  • When examples or wording diverge, the source code and released package behavior are authoritative; this English README is the primary package-facing entry, and the Chinese guide explains the same surface for Chinese readers.

Error Reference

const {
  RouteConflictError,
  InvalidPathError,
  InvalidMethodError,
  InvalidStoreIdError,
} = require('route-core')
Error classCodeTrigger
RouteConflictErrorERR_ROUTE_CONFLICTDuplicate registration for the same method and normalized route shape
InvalidPathErrorERR_INVALID_PATHWildcard route used while allowWildcard is false
InvalidMethodErrorERR_INVALID_METHODEmpty method string
InvalidStoreIdErrorERR_INVALID_STORE_IDstoreId is not a non-negative safe integer

Changelog

License

MIT

FAQs

Package last updated on 27 May 2026

Related posts