npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

instant-router

v1.0.1

Published

Ultra fast router using RadixTree data structure to match incoming HTTP requests.

Downloads

28

Readme

Instant Router

Ultra fast router to match incoming HTTP requests and generate urls.

  • Uses a radix tree datastructure.
  • Highly optimized to match static routes, check the benchmarks.
  • UrlGenerator for relative/absolute urls.
  • No middlewares.
  • Framework independent.

Summary

Install

npm install instant-router
yarn add instant-router

Usage

Matching

import Router, { HttpContext, RouteDefinition, MethodNotAllowedError, RequestContext, ResourceNotFound } from 'instant-router';
import http, { IncomingMessage, ServerResponse } from 'node:http';

const routes: RouteDefinition[] = [
  {
    path: '/static',
    controller: ({ res }: HttpContext) => res.end('Hello, World!')
  },
  {
    path: '/users/:name',
    requirements: { name: '[a-zA-Z]+' },
    controller: ({ params, res }: HttpContext) => res.end(`Hello ${params.name}!`)
  }
]

const router = new Router()
routes.forEach(route => router.addRoute(route))

http.createServer((request: IncomingMessage, res: ServerResponse) => {
  try {
    const req = RequestContext.fromIncomingMessage(request)
    const { controller, params } = router.match(req);

    controller({ req, res, params })
  } catch (error) {
    console.error(error)
    if (error instanceof ResourceNotFound) {
      res.writeHead(404)
      res.end('404 - Resource not found')
    } else if (error instanceof MethodNotAllowedError) {
      res.writeHead(405)
      res.end(`405 - Method ${request.method} not allowed`)
    } else {
      res.writeHead(500)
      res.end('500 - Internal server error')
    }
  }
}).listen(3000, () => console.log('Listening on localhost:3000'))

:arrow_up::arrow_up::arrow_up: Go back to summary

Url generation

import Router from 'instant-router';

const router = new Router();

// Name the routes to use UrlGenerator
router.addNamedRoute('comment', {
  path: '/posts/:postId/comments/:commentId',
  methods: ['GET'], // default
  controller: () => {}
})

const url = router.generateUrl('comment', 
  { 
    postId: 1, 
    commentId: 10, 
    foo: 1 // This is not a route parameter, so it will be added to the url as a query param.
  },
  { 
    isAbsolute: true,
    // scheme: 'http',
    // host: 'localhost',
    // port: 3000
  }
)

console.log(url); // "http://localhost:3000/posts/1/comments/10?foo=1"

:arrow_up::arrow_up::arrow_up: Go back to summary

Router class

Adding routes

To add a route, you must use the addRoute method.

Example:

router.addRoute({
  path: '/users/:id',
  methods: ['GET'],
  requirements: { name: '\\d+' },
  controller: ({ res }: HttpContext) => res.end("Matched!")
})

path

  • Should be a string that represents the URL path for the route.
  • Can contain parameters in the format of /my-path/:parameterName.
  • Parameters in the URL path can be constrained by a regex using the requirements property.

methods

  • Is an optional string or an array of strings that represents the HTTP methods. Default: GET
  • Valid methods are defined automatically via the route configuration or explicitly using the addMethods method.

requirements

  • Is an optional object that defines validation requirements for parameters in the URL path.
  • Keys (parameter name) and values (regex) must be of type string.

:warning: The router automatically adds the start and end delimiters, do not add them by yourself.

Bad: "^\\d+$", good: "\\d+"

controller

  • Should be a function that represents the handler for the route.
  • Take HttpContext ({req, res, params}) as an argument. Note that it will be your responsibility to call the controller.

:arrow_up::arrow_up::arrow_up: Go back to summary

Matching

To try to match a route from an HTTP request, you must use the match method.

Example:

const context = new RequestContext('/users/1', 'GET')
const { controller, params } = router.match(context);

The RequestContext class help you to pass relevant HTTP request data to the match method. You can read its documentation here.

The match method returns a MatchedTreeNode containing the properties controller and params.

Errors

  • If the request HTTP method does not match any existing route, a MethodNotAllowedError is triggered.
  • If no route is matched, a ResourceNotFound error is triggered.

See Usage for examples.

:arrow_up::arrow_up::arrow_up: Go back to summary

Generate urls

To generate urls, you can use the generateUrl method.

Example:

// Name the routes to use UrlGenerator
router.addNamedRoute('comment', {
  path: '/posts/:postId/comments/:commentId',
  methods: ['GET'],
  controller: () => {}
})

const url = router.generateUrl('comment', 
  { postId: 1, commentId: 10 }, 
  { isAbsolute: true }
)

name

Is the name of the route to generate the URL for.

parameters

Optional object containing key-value pairs of route parameter names. If a passed parameter does not match any route parameter, it is added to the url as a query parameter.

options

type urlGeneratorOptions = {
  isAbsolute?: boolean
  scheme?: string
  host?: string
  port?: number
}

You can set the default options directly when you instantiate Router:

const options = {
  urlGenerator: {
    isAbsolute: true,
    scheme: 'https', // must be defined if isAbsolute is true
    host: 'example.com', // must be defined if isAbsolute is true
    port: 443 // optional even if isAbsolute is true
  }
};

const router = new Router(options);

:arrow_up::arrow_up::arrow_up: Go back to summary

RequestContext class

The RequestContext class help you to pass relevant HTTP request data to the match method.

Usage:

import http from 'node:http';
import { RequestContext } from 'instant-router'

http.createServer((req, res) => {
    const context = RequestContext.fromIncomingMessage(req)

    console.log(context.path) // "/users/1"
    console.log(context.method) // "POST"
})

By default, RequestContext supports only the following http methods:

GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH

But you can add more freely via the static property availableHttpMethods:

RequestContext.availableHttpMethods.push("FOO")

const context = new RequestContext("/hello", "FOO")
console.log(context.method) // "FOO"

:arrow_up::arrow_up::arrow_up: Go back to summary

Benchmarks

Benchmark comparisons, adapted from: https://github.com/delvedor/router-benchmark/tree/master/benchmarks

Machine

linux x64 | 8 vCPUs | 7.6GB Mem

Software versions

  • node: 18.14.2
=====================
 instant-router benchmark
=====================
short static: 232,598,222 ops/sec
static with same radix: 56,142,144 ops/sec
dynamic route: 3,162,154 ops/sec
mixed static dynamic: 3,150,744 ops/sec
long static: 55,622,346 ops/sec
all together: 1,380,149 ops/sec

=======================
 find-my-way benchmark
=======================
short static: 17,099,450 ops/sec
static with same radix: 6,243,196 ops/sec
dynamic route: 3,293,502 ops/sec
mixed static dynamic: 4,176,557 ops/sec
long static: 3,863,775 ops/sec
all together: 876,926 ops/sec

=======================================================
 express router benchmark (WARNING: includes handling)
=======================================================
short static: 2,111,590 ops/sec
static with same radix: 1,832,877 ops/sec
dynamic route: 1,087,600 ops/sec
mixed static dynamic: 831,342 ops/sec
long static: 860,493 ops/sec
all together: 221,828 ops/sec

:arrow_up::arrow_up::arrow_up: Go back to summary