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

subroute

v1.0.1

Published

Type-safe routing in typescript

Downloads

9

Readme

subroute

This is library for type safe routing that addressing two primary concerns:

  1. Parsing

    Type safe parseing of routes - Extracting (typed) parameters so that type checker is able to report any missuse.

  2. Linking / Formatting

    Type safe formating of hyper links - Type checker is able to report if any parameter is missing or mistyped.

The problem

Here is a simlpe example that uses a routing system of Express web framework for Node.js:

Disclaimer: There is no intention to diminish or crticize Express, it's an excellent library. As a matter of fact pointed out shortcomings are shortcomings of an untyped nature of JS, which is what Express is tailored for.

That being said, raising popularity of TypeScript provides new opportunities and there is no better way to illustrate them than to compare it to an established solution.

const express = require("express")
const app = express()

app.get("/", (request, response) => {
  response.send(`<a href='/calculator/313/+/3'>Calculate 313 + 3</a>`)
})

app.get("/calculator/:a/+/:b", (request, response) => {
  const { a, b } = request.params
  response.send(`${parseFloat(a) + parseFloat(b)}\n`)
})

Note: Express does not actually allow /+/ path segments, and you would have to use /plus/ instead, but for the sake of this example lets prentend it does

Parsing

There are multiple issues with this approach, that can lead to mistakes which can sneak into production:

  • Handling of parameters in routes is too repetitive.

    Declaring a route parameter requires choose a name, which you must later repeat to get it from request.params. Mistyping the name of the parameter is a mistake which is not caught by the type checker (even if used). It is just too easy to make changes which would update names in some places and not other causing program to misbehave.

  • Request handler needs to parse route parameters.

    All parameter values are passed as strings to a handler, which then needs to be parsed, handling all possible edge cases (In our example /calculator/313/+/bob would respond with NaN :)

Linking

Even if we manage to keep parameter nameing in sync across the code base and excell at parsing their values, there still more that could go wrong:

  • Route changes affect hyper links.

    Let's say we had to switch to prefix notation for our calculator and switched from URLs like /calculator/313/+/3 to /calculator/plus/313/3 it's just too easy to forget to update a link in our / route.

Solution

import { GET, int, format } from "subroute"
import express from "express"

const example = { a: 313, b: 3 }

const index = GET`/`(() =>
  `<a href='${format(calc.route, example)}'>
    Calculate ${example.a} + ${example.b}
  </a>`)
)

const calc = GET`/calculator/${{ a: int }}/+/${{ b: int }}`(({ a, b }) =>
  `${a + b}`
)

const router = index.or(caluclator)

const app = express()
app.use((request, response) => {
  const result = router.handle(request)
  response.send(result)
})

Presented solution attempts to illustrate building blocks that can be used for:

  1. Parsing route parameters in a type safe way.

    Type checker that route handler expects parameters that were parsed.

  2. Format hyper-links in type safe way.

    Links are formated by calling format(calc, {a:313, b:3}) on a route allowing type checker to report any missmatch in type or number of parameters passed.

This elliminates all of the problems pointed out with original example:

  • No way to mistype parameter names, at least not without type checker reporting that as an error.

  • No need to parse route parameters as our routes are typed parsers already.

    Note: Route as presented in the example won't match /calculator/313/+/bob since bob is not an int).

  • Route changes will not break links.

    Links are formatted from the routes themselves, so if number or order of parameters changes type checker will be at your service and tell you all the places you need to update. For example if we update our routing to prefix notation only our route definition will change & all the links will continue to work as expected:

    - const calc = route`/calculator/${{ a: int }}/+/${{ b: int }}`(
    + const calc = route`/calculator/plus/${{ a: int }}/${{ b: int }}`(

Prior Art

This was initially inspired by url-parser package Elm library, but later on moved towards the type safe routing approach used in Spock - A lightweight Haskell web framework. Both are great source of inspiration for this work.