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 🙏

© 2026 – Pkg Stats / Ryan Hefner

routup

v6.0.0

Published

Routup is a minimalistic http based routing framework.

Readme

Routup banner

Routup 🧙‍

npm version main codecov Known Vulnerabilities Conventional Commits

Routup is a minimalistic, runtime-agnostic HTTP routing framework for Node.js, Bun, Deno, Cloudflare Workers, and Service Workers. Handlers return values directly — routup converts them to Web Response objects automatically, with built-in support for ETags, content negotiation, per-handler timeouts, and cooperative cancellation via AbortSignal.

Table of Contents

Installation

npm install routup --save

Features

  • 🚀 Runtime agnostic — Node.js, Bun, Deno, Cloudflare Workers, Service Workers
  • 🌐 Web-standard APIs — built on Request / Response for portability
  • 📝 Return-based handlers — return strings, objects, streams, Blobs, or Response directly
  • Async middleware — onion model with event.next()
  • 🧭 Pluggable router & cacheLinearRouter (default), TrieRouter, or SmartRouter (auto-selects); opt-in LRU lookup cache
  • ⏱️ Per-handler timeouts — bounded execution with AbortSignal cooperative cancellation
  • 🏷️ Automatic ETag & 304 — strong/weak ETags out of the box, configurable per app or disabled entirely
  • 🤝 Content negotiation — accept, accept-language, accept-encoding, accept-charset helpers
  • 📡 Streaming & SSEReadableStream responses and createEventStream() for server-sent events
  • 📂 Static file servingsendFile() with ETag, range, and MIME detection
  • 🔌 Plugin system — extend with reusable, installable plugins
  • 🌉 Express middleware bridge — wrap legacy (req, res, next) handlers via fromNodeHandler()
  • 🧰 Tree-shakeable helpers — import only what you use
  • 📁 Nestable apps — modular route composition with mount paths
  • 👕 TypeScript first — fully typed API with generics
  • 🤏 Minimal footprint — small core, no bloat

Documentation

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

Usage

Handlers

Handlers receive an event and return a value. Routup converts the return value to a Web Response automatically.

Shorthand

import { App, defineCoreHandler, defineErrorHandler, serve } from 'routup';

const app = new App();

app.get('/', defineCoreHandler(() => 'Hello, World!'));
app.get('/greet/:name', defineCoreHandler((event) => `Hello, ${event.params.name}!`));
app.use(defineErrorHandler((error) => ({ error: error.message })));

serve(app, { port: 3000 });

Verbose

import { App, defineCoreHandler, serve } from 'routup';

const app = new App();

app.use(defineCoreHandler({
    path: '/',
    method: 'GET',
    fn: () => 'Hello, World!',
}));

app.use(defineCoreHandler({
    path: '/greet/:name',
    method: 'GET',
    fn: (event) => `Hello, ${event.params.name}!`,
}));

serve(app, { port: 3000 });

Return Values

| Return type | Response | |-------------|----------| | string | text/plain | | object / array | application/json | | Response | Passed through as-is | | ReadableStream | Streamed to client | | Blob | Sent with blob's content type | | null | Empty response (status from event.response) |

Middleware

Middleware calls event.next() to continue the pipeline:

app.use(defineCoreHandler(async (event) => {
    console.log(`${event.method} ${event.path}`);
    return event.next();
}));

Pluggable router and cache

The route table is pluggable via the router option. The default LinearRouter is best for small apps; swap to TrieRouter for radix-trie matching on apps with many routes, or SmartRouter to auto-select between the two based on the registered route shape at first lookup. Each router accepts an optional cache for memoizing lookups — opt-in via LruCache (or any ICache implementation); pass null to disable.

import { App, TrieRouter, LruCache, defineCoreHandler } from 'routup';

const app = new App({
    router: new TrieRouter({ cache: new LruCache() }), // omit `cache` for no memoization
});

Timeouts and cancellation

Configure a global timeout for the whole pipeline, a default per-handler timeout, or both. When a deadline fires, event.signal is aborted so handlers can cooperatively cancel signal-aware work; if nothing recovers in time, routup returns 408 Request Timeout.

const app = new App({
    timeout: 30_000,         // entire request
    handlerTimeout: 5_000,   // default per handler; handlers can narrow further
});

app.get('/fetch', defineCoreHandler(async (event) => {
    const res = await fetch('https://api.example.com', { signal: event.signal });
    return res.json();
}));

Runtimes

Routup runs on Node.js, Bun, Deno, and Cloudflare Workers. In most cases, import from routup:

import { App, defineCoreHandler, serve } from 'routup';

const app = new App();
app.get('/', defineCoreHandler(() => 'Hello, World!'));
serve(app, { port: 3000 });

For runtime-specific APIs (e.g. toNodeHandler), use the corresponding entrypoint like routup/node.

Templates

Scaffold a new project from any starter in routup/templates with degit:

npx degit routup/templates/node-api my-app

| Template | Runtime | Highlights | |----------|---------|------------| | node-api | Node.js >=22 | JSON API with @routup/body | | cloudflare-worker | Cloudflare Workers | Configured with wrangler | | bun-decorators | Bun | Class-based routing via @routup/decorators |

Plugins

Routup is minimalistic by design. Plugins extend the framework with additional functionality.

| Name | Description | |------|-------------| | assets | Serve static files from a directory | | basic | Bundle of body, cookie, and query plugins | | body | Read and parse the request body | | cookie | Read and parse request cookies | | cors | Cross-Origin Resource Sharing (CORS) middleware | | decorators | Class, method, and parameter decorators | | i18n | Translation and internationalization | | logger | HTTP request logger with morgan-compatible tokens and presets | | prometheus | Collect and serve Prometheus metrics | | query | Parse URL query strings | | rate-limit | Rate limit incoming requests | | rate-limit-redis | Redis adapter for rate-limit | | swagger-ui | Mount swagger-ui-dist on any path |

Comparison

How routup stacks up against other popular Node.js routing frameworks. This is a best-effort summary; check each project's docs for the full picture.

| | routup | Hono | Express | Fastify | |-----------------------------------------|-------------------|--------------------------|----------------------------------|--------------------------------| | Runtimes | Node, Bun, Deno, Cloudflare, Service Worker | Node, Bun, Deno, Cloudflare, Lambda, Vercel | Node | Node | | Web-standard Request / Response | ✅ | ✅ | ❌ | ❌ | | Return-based handlers | ✅ | ✅ | ❌ | ❌ | | TypeScript-first | ✅ | ✅ | community types | ✅ | | Tree-shakeable helpers | ✅ | ✅ | ❌ | ❌ | | Onion middleware (next()) | ✅ | ✅ | linear next() | lifecycle hooks | | Pluggable router (linear / trie) | ✅ linear, trie, or auto-select | trie only | linear only | radix only | | Built-in ETag + 304 | ✅ | ❌ | via plugin | via plugin | | Per-handler timeout + AbortSignal | ✅ | ❌ | ❌ | server-level | | Class-based routes (decorators) | ✅ via plugin | ❌ | ❌ | ❌ | | Express middleware bridge | ✅ fromNodeHandler | ❌ | n/a | limited | | Schema validation built-in | ❌ | ❌ | ❌ | ✅ |

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.