Routup banner

Routup πŸ§™β€

npm version main codecov Known Vulnerabilities Conventional Commits

Routup is a fast, lightweight, runtime agnostic and asynchronous routing framework. Helpers provide additional functionalities to interact with the request and manipulate the response.

It can be used independently of the selected runtime environment (Node.Js, Bun, ... ) πŸŽ‰. Moreover, it is even 228% faster than Express (more).

Table of Contents

Installation

npm install routup --save

Features

  • πŸš€ runtime agnostic (Node.JS, Bun, Deno, ...)
  • πŸ“ different handler types (base & error)
  • ✨ promise (async) support for core- & error-handlers
  • πŸ”Œ powerful plugin system
  • 🧰 tree shakeable response & request helpers
  • 🀝️ different handler declaration styles (shorthand & verbose)
  • πŸ“ nestable routers
  • πŸ‘• TypeScript support
  • 🀏 minimalistic to fit into any solution with minimum overhead
  • & much more

Documentation

To read the docs, visit https://routup.net

Usage

The following examples are intended to give a small insight into the use of the framework. However, it is highly recommended to read the documentation, as all concepts and basics are taught there.

Handlers

Both core and error handlers, can be defined in two different ways. Core handler functions can have up to 3 arguments (req, res, next) whereas error handler functions can have up to 4 arguments (err, req, res, next). This should be familiar to anyone who has used express before.

Shorthand

With the shorthand variant, only the handler function is passed as argument to the coreHandler & errorHandler function.

import { createServer } from 'node:http';
import {
    coreHandler,
    createNodeDispatcher,
    errorHandler,
    Router,
    useRequestParam
} from 'routup';

const router = new Router();

router.get('/', coreHandler(() => 'Hello, World!'));
router.get('/greet/:name', coreHandler((req) => `Hello, ${useRequestParam(req, 'name')}!`));
router.use(errorHandler((err) => `An error with statusCode ${err.statusCode} occured.`));

const server = createServer(createNodeDispatcher(router));
server.listen(3000)

Verbose

The verbose variant is more complex, but offers the possibility to set additional information like path, method, ... in the handler definition.

import { createServer } from 'node:http';
import {
    coreHandler,
    createNodeDispatcher,
    errorHandler,
    Router,
    useRequestParam
} from 'routup';

const router = new Router();

router.get(coreHandler({
    path: '/',
    fn: () => 'Hello, World!',
}));

router.get(coreHandler({
    path: '/greet/:name',
    fn: (req) => `Hello, ${useRequestParam(req, 'name')}!`
}))

router.use(errorHandler({
    fn: (err) => `An error with statusCode ${err.statusCode} occured.`
}))

const server = createServer(createNodeDispatcher(router));
server.listen(3000)

Runtimes

It is possible to use any javascript runtime environment. Below are examples for Bun and Deno. These use the web dispatcher to submit requests based on the web api. Besides the node- & web-dispatcher, there is also a plain dispatcher that underlies the web dispatcher, which can be controlled via a simple API.

Bun

import {
    coreHandler,
    createWebDispatcher,
    Router
} from 'routup';

const router = new Router();

router.get('/', coreHandler(() => 'Hello, World!'));

const dispatch = createWebDispatcher(router);

Bun.serve({
    async fetch(request) {
        return dispatch(request);
    },
    port: 3000,
});

Deno

import {
    coreHandler,
    createWebDispatcher,
    Router
} from 'routup';

const router = new Router();

router.get('/', coreHandler(() => 'Hello, World!'));

const dispatch = createWebDispatcher(router);

const server = Deno.listen({
    port: 3000
});
for await (const conn of server) {
    const httpConn = Deno.serveHttp(conn);

    for await (const requestEvent of httpConn) {
        const response = await dispatch(
            requestEvent.request
        );
        requestEvent.respondWith(response);
    }
}

Plugins

According to the fact that routup is a minimalistic framework, it depends on plugins to cover some typically http framework functions, which are not integrated in the main package.

Name Description
assets Serve static files from a directory.
body Read and parse the request body.
cookie Read and parse request cookies and serialize cookies for the response.
decorators Create request handlers with class-, method- & parameter-decorators.
prometheus Collect and serve metrics for prometheus.
query Read and parse the query string of the request url.
rate-limit Rate limit incoming requests.
rate-limit-redis Redis adapter for the rate-limit plugin.
swagger Serve generated docs from URL or based on a JSON file.

Benchmarks

  • CPUs: 24
  • RAM: 63.9GB
  • Node: v18.16.0
  • Date: Wed Sep 13 2023 15:11:58 GMT+0200 (MitteleuropΓ€ische Sommerzeit)
  • Method: autocannon -c 100 -d 40 -p 10 localhost:3000 (two rounds; one to warm-up, one to measure)
Package Requests/s Latency (ms) Throughput/MB
http 61062 15.87 10.89
fastify 59679 16.26 10.70
koa 45763 21.35 8.16
routup 43881 22.29 8.87
hapi 41374 23.67 7.38
express 13376 74.18 2.39

Benchmarks were generated using autocannon. To recreate the results, this can be done using the benchmarks' repository.

Contributing

Before starting to work on a pull request, it is important to review the guidelines for contributing and the code of conduct. These guidelines will help to ensure that contributions are made effectively and are accepted.

License

Made with πŸ’š

Published under MIT License.