GithubHelp home page GithubHelp logo

subroute's Introduction

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.

subroute's People

Contributors

github-actions[bot] avatar gozala avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.