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

parameterized-fs-routing

v1.0.0

Published

filesystem based routing engine

Downloads

70

Readme

parameterized-fs-routing

filesystem based routing engine

Features

  • parameterized paths
  • supports multiple route matching (e.g. to implement an access schema in an explicit way)
  • supports multiple routing roots (e.g. to implement server side event trees beside the url routing)
  • integrates with plain node and frameworks like express

Advantages

  • small (~1.8kb minified)
  • fast
  • dependency free
  • all features are documented and covered by tests
  • no config
  • optimized for speed in prod env
  • optimized for comfort in dev env

set up the routing

initialization

Init the router with one or more root routing directories.

binding

Call the router with the req and res arguments to get the response.

example set up with plain node

const http = require('http')
const routing = require('parameterized-fs-routing')

const rootDirs = ['/home/project/src/routing-headless', '/home/project/src/routing-browser']
const router = await routing.init(rootDirs)

const server = http.createServer(async (req, res) => {
    const response = await router(req, res)

    res.end(response)
});

server.listen(3000, '127.0.0.1', () => {});

example set up with express

const express = require('express')
const routing = require('parameterized-fs-routing')

const app = express()

const directories = ['/home/project/src/routing-headless', '/home/project/src/routing-browser']
const router = await routing.init(directories)

app.use(async (req, res, next) => {
    const response = await router(req, res)
    
    res.send(response)
    
    next()
})

app.listen(3000, () => {})

the routing process

Every routing process starts in the first of the root directories. If there is a matching controller it is stacked to a fifo controller-pipe.

Then the tree is traversed by matching url-parts to directory names. In every directory matching controllers are stacked to the controller-pipe.

If one tree is traversed the next root directory is processed.

parameterizing directories

Directory names starting with a colon (:) are treated as parameters. E.g. the url path (without the domain) is admin/board/201/87 and the controller path (without the root) is admin/board/:board-id/:user-id/index.js then the arguments variable route.args holds { 'board-id':'201', 'user-id':'87' }.

Exact matches are traversed before parameterizing directories. Thus parameterizing directories can be used as fallback.

If there is more than one parameterizing directory the order of traversal may change on runtime. (It is recommended against using more than one parameterizing directory.)

matching controller names

A controller is every file beneath a routing directory root that ends with .js .mjs or .cjs and either starts with the lower case request method name or with index.

index files do not care about the type of request. For all other files req.method.toLowerCase() is used to find the right controller file.

If there is more than one matching controller file in one directory the order in which they are stacked to the controller-pipe may change on runtime.

matching controller functions

Every controller file has to export one or more function. They have to be named like req.method.toLowerCase() e.g. post, get, patch, etc. One exception is del which is the function name for the request method DELETE.

If the controller exports a function named controller it is used as fallback if the request method does not match any exported function.

If no function matches the request method and no fallback exists the controller is silently dropped from the controller-pipe.

traversing the controller-pipe

Like in express to the matching controller function a next function is passed as third parameter. By calling next() the next controller in the controller-pipe is called.

If a parameter is passed to the next function it is used for matching subsequent controller names and functions. This serves two purposes:

  1. The controller selecting string can change from post to get as soon as a login form is handled successfully. (No need to trigger a redirect after login.)
  2. It allows the controller selecting logic to be branched with ease. next('foo') would call only foo and index files with foo or controller named functions in the controller-pipe.

the controller function call

One can use async or normal functions as controller functions.

Five parameters are passed to the controller function: req, res, ǹext, route and routes.

req and res are the request and response objects the router is called with.

next is a function without arguments to call the next one in the controller-pipe and receive its response.

route is an object that holds information about the routing process:

  • route.path holds the full matched file system path.
  • route.params is an array of the remaining url parts.
  • route.args is an object of the matched parametrizing directories and its url counterparts.

routes holds all matched routes. Ones that are already called as well as those in the controller-pipe.

example controller

module.exports = { controller, post }

// will be called if it is not a POST request
function controller(req, res, next, route, routes) {
    res.setHeader('Content-Type', 'text/html; charset=UTF-8')

    const accountId = route.args['account-id']

    return `Hello world from account ${ accountId }!`
}

async function post(req, res, next) {
    res.setHeader('Content-Type', 'text/html; charset=UTF-8')
    
    // wait 500 milliseconds and then call the next controller
    return await (new Promise(resolve => setTimeout(() => {
        resolve(next()) 
    }, 500)))
}

differences in prod and env environment

If the process environment variable NODE_ENV is set to production all routes and controller are loaded on initialization thus minimizing the file system load at runtime. Mark that there is a drawback: Changes to the routes or controllers have no effect after initialization is done.

If the process does not run in production mode every routing call is processed via the current file system.