
Security News
arXiv Is Rate Limiting Authors Following a Flood of AI Slop Submissions
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.
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.
add, find, lookup, and allowed.routePath, which is useful for low-cardinality values such as req.route.npm install route-core
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'
})
createRouter(options?)Creates and returns a new Router instance.
function createRouter(options?: RouterOptions): Router
RouterOptions| Option | Type | Default | Description |
|---|---|---|---|
ignoreTrailingSlash | boolean | true | Treats /foo and /foo/ as the same route |
caseSensitive | boolean | false | When false, the matcher normalizes path keys to lowercase |
maxParamLength | number | 500 | Maximum decoded length for a parameter segment; overflow returns null |
allowWildcard | boolean | true | When false, wildcard routes throw InvalidPathError |
router.add(method, path, storeId)Registers a route.
router.add(method: string, path: string, storeId: number): void
| Parameter | Description |
|---|---|
method | HTTP method. Built-ins include GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, CONNECT, and ANY. Methods are normalized to uppercase. |
path | Route pattern. Supports static segments, :param named parameters, and trailing *name wildcards. |
storeId | Non-negative safe integer returned on match. Your framework can map it to handlers or stores. |
Errors:
| Error class | Code | Trigger |
|---|---|---|
RouteConflictError | ERR_ROUTE_CONFLICT | Duplicate registration for the same method and normalized route shape |
InvalidPathError | ERR_INVALID_PATH | Wildcard route used while allowWildcard is false |
InvalidMethodError | ERR_INVALID_METHOD | Empty method string |
InvalidStoreIdError | ERR_INVALID_STORE_ID | storeId 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:
maxParamLength.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 value | Meaning |
|---|---|
true | A route matched and onMatch was called |
false | No 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 value | Meaning |
|---|---|
null | No 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.
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-core splits on the raw / delimiter before decoding parameter values, so %2F stays inside the matched segment.
| Input handling | Behavior |
|---|---|
| query/hash | Ignored before matching |
| percent decoding | Applied only after a route is matched |
%2F | Preserved inside the segment and decoded to / in params |
caseSensitive=false | Affects matching keys only; returned params keep request casing |
maxParamLength | Checked against the decoded parameter or wildcard value |
| Pattern type | Example | Match behavior |
|---|---|---|
| Static | /users/profile | Exact 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.
When multiple patterns can match the same request path, route-core uses this order:
static > :param > *wildcard
ANY MethodANY 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' }
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
}
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 })
Documentation linkage:
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.const {
RouteConflictError,
InvalidPathError,
InvalidMethodError,
InvalidStoreIdError,
} = require('route-core')
| Error class | Code | Trigger |
|---|---|---|
RouteConflictError | ERR_ROUTE_CONFLICT | Duplicate registration for the same method and normalized route shape |
InvalidPathError | ERR_INVALID_PATH | Wildcard route used while allowWildcard is false |
InvalidMethodError | ERR_INVALID_METHOD | Empty method string |
InvalidStoreIdError | ERR_INVALID_STORE_ID | storeId is not a non-negative safe integer |
MIT
FAQs
Minimal, zero-runtime-dependency routing engine for Node.js frameworks.
The npm package route-core receives a total of 27 weekly downloads. As such, route-core popularity was classified as not popular.
We found that route-core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.

Research
/Security News
A new GhostAction wave hits hundreds of GitHub repos, expanding CI/CD secret theft to cloud and AI credentials in source code and git history.

Research
/Security News
Tensorlake npm SDK version 0.5.144 was compromised in a ChainDrop / Shai-Hulud attack, delivering credential-stealing malware.