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

@qubitcodes/qcrouter

v1.3.1

Published

Type-safe routing compiler and middleware chainer for Next.js and modern JavaScript runtimes.

Downloads

574

Readme

@qubitcodes/qcrouter 🚀

@qubitcodes/qcrouter is a lightweight, zero-dependency routing compiler and sequential middleware chainer. Written in strict TypeScript, it compiles dynamic route trees into pre-compiled RegExp matchers for fast request-time matching.

It is framework-agnostic, working in Node.js, Bun, Deno, Astro, SvelteKit, Hono, and Next.js (App Router Edge Middleware).


Key Features

  • Compiled Matchers: Compiles route path patterns (e.g. /users/:id) once at boot-time into cached RegExp matchers to avoid request-time allocations.
  • Agnostic Core: Middleware runner matches standard Web API Request and Response interfaces, making it compatible with any modern JS runtime.
  • Type Safety: Provides compile-time autocompletion of route names and path parameters in your editor.
  • Next.js Edge Compatibility: Includes a dedicated proxy chainer sub-module (@qubitcodes/qcrouter/next) compatible with Edge Middleware runtimes.
  • Dual Bundle: Ships with ES Modules (esm/) and CommonJS (cjs/) targets.

Installation

npm install @qubitcodes/qcrouter

Quick Start (Automated Setup)

Instead of manually creating proxy files, hooks, or configurations, qcrouter includes an interactive CLI setup wizard that automates the integration for you:

npx qcrouter init

What this automated command does:

  1. Auto-Detects Your Web Stack: Automatically scans your package.json to identify if you are running in Next.js, Astro, SvelteKit, or Hono.
  2. File Scan: Scans dynamically for existing hook/middleware files (checking both the src/ directory and root). If not found, it checks if a src/ directory is present to safely initialize the integration file inside it (e.g. src/hooks.server.ts or src/proxy.ts), falling back to your project root otherwise.
  3. Safety Backup: If a target file already exists, the CLI will log a warning and automatically save a timestamped backup (e.g. src/proxy.ts.bak-2026-05-28-175325) so you never lose your code!
  4. Writes Templates: Automatically writes framework-specific template integrations matching Next.js proxies, Astro middlewares, SvelteKit Hooks, Hono registers, or a helpful Custom Node.js Express/Fastify initializer.
  5. Generates Configs: Generates boilerplate qcRouter.config.ts and middleware.config.ts in your project root!

Architectural Philosophy: Pure Router vs. Utility Overlay

Because @qubitcodes/qcrouter is 100% framework-agnostic and built using standard Web API signatures (Request, Response), it adapts to the design pattern of your specific web stack:

1. Traditional Frameworks (Hono, Express, Fastify, Client-Side SPAs)

  • Status: Primary Routing Engine
  • Role: In standard declaration-based architectures, @qubitcodes/qcrouter is your sole and absolute routing engine. When you change a path pattern (e.g. from /terminals to /terminals1) in your central config tree, it instantly updates the live request matcher, route controllers, and dynamic URL generation (route()) in one single step.

2. File-System Based Frameworks (Next.js App Router, Astro, SvelteKit)

  • Status: MVC Utility Overlay & Middleware Proxy
  • Role: Because these frameworks handle visual page loading using physical directories on disk (e.g., src/app/terminals/page.tsx), the framework's filesystem is the visual routing engine.
  • How it is consumed: In these environments, you do not use qcrouter to load or serve visual pages. Instead, you leverage it to enforce clean MVC architecture:
    1. Named Route Resolvers (route()): Dynamically resolves named route keys within your project components (e.g. headers, footer links) instead of hardcoding raw strings. If a path changes, you simply rename the physical folder to match and update your config; all dynamic UI links update automatically!
    2. Edge Middleware Proxy Gateway (createNextProxy): Intercepts incoming requests at the Edge (middleware.ts) before they load pages, parsing paths and chaining sequential route-specific middleware pipelines (such as token validation, rate limiting, and request logs).

1. Agnostic Core Setup (Standard JS/TS, Hono, Astro, SvelteKit)

Define your routes configuration and register it with defineRouter:

// qcRouter.config.ts
import { defineRouter } from '@qubitcodes/qcrouter';

// 1. Declare route tree configuration with 'as const'
export const qcRouterConfig = {
	web: {
		prefix: '',
		middleware: ['log'],
		routes: {
			home: '/',
			profile: '/users/:id',
		}
	},
	api: {
		prefix: '/api',
		middleware: ['auth'],
		routes: {
			users: {
				path: '/users',
				method: 'GET'
			}
		}
	}
} as const;

export const qcRouter = defineRouter(qcRouterConfig);
export const { flatRoutes, route } = qcRouter;

Now, in any other component or script, import { route } from your local qcRouter.config.ts and get type-safe autocompletion on route names and required path parameters:

// AnyComponent.tsx
import { route } from './qcRouter.config';

// Resolves to "/users/123" with compile-time verification
const url = route('profile', { id: 123 });

For non-web groups, route names are prefixed by the group key:

route('api.users');

2. Next.js App Router Edge Proxy Gateway (qcrouter/next)

Use the dedicated Next.js Edge adapter to create a secure routing proxy by importing the pre-compiled flatRoutes from your config:

// src/proxy.ts (Next.js Edge Middleware Proxy)
import { NextRequest, NextResponse } from 'next/server';
import { createNextProxy, NextMiddlewareHandler } from '@qubitcodes/qcrouter/next';
import { flatRoutes } from '../qcRouter.config';
import { MIDDLEWARE_MAP } from '../middleware.config';

// Create the Edge Proxy Gateway Runner
export const proxy = createNextProxy({
	flatRoutes,
	middlewareMap: MIDDLEWARE_MAP,
	fallbackHandler: () => NextResponse.next() // Proceed to pages if no route matched
});

3. Dynamic URL Parameters (Single & Multiple)

qcrouter supports resolving routes with single or multiple path parameters. Autocomplete typing is preserved automatically during editor resolution:

// Define path containing multiple unique parameters:
const config = {
	web: {
		routes: {
			postComment: '/users/:userId/posts/:postId/comments/:commentId'
		}
	}
} as const;

export const { route } = defineRouter(config);

// Resolving multiple parameters with complete compile-time validation:
const url = route('postComment', {
	userId: 'jkthoppil',
	postId: 987,
	commentId: 42
});
// Outputs: "/users/jkthoppil/posts/987/comments/42"

4. Route Grouping, Nesting, & Middleware Exceptions

You can recursively group and nest routes inside the configuration. Nested routes inherit prefix names, path prefixes, and parent middleware arrays:

const config = {
	admin: {
		prefix: '/admin',
		middleware: ['auth', 'adminRole'], // Inherited by all children
		routes: {
			dashboard: '/dashboard',       // Path: /admin/dashboard
			analytics: {
				path: '/analytics',        // Path: /admin/analytics
				middleware: ['perfLog']    // Runs: ['auth', 'adminRole', 'perfLog']
			},
			login: {
				path: '/login',            // Path: /admin/login
				withoutMiddleware: ['auth', 'adminRole'] // Exempts itself from parent restrictions
			}
		}
	}
} as const;

5. Library Engine vs. Application Layer (Architecture boundaries)

When you run npm install @qubitcodes/qcrouter, the installer downloads only the compiled compiler and pipeline executor (dist/).

  • The Library (@qubitcodes/qcrouter): Is non-intrusive. It does not auto-inject file layouts (like a src/middlewares folder or proxy.ts) into your project.
  • The Application: You create your own local src/proxy.ts and src/middlewares/ directory. You import @qubitcodes/qcrouter inside your proxy gateway to orchestrate your custom, environment-specific middleware callback functions.

6. Multi-Stack Integrations (Express, Hono, Astro, SvelteKit)

Because the core sequential chainer is 100% framework-agnostic, you can easily integrate it to run middleware pipelines in standard Node.js servers, edge frameworks, and serverless environments using the pre-compiled flatRoutes and MIDDLEWARE_MAP from your config:

Express Middleware Setup

import express from 'express';
import { runMiddlewareChain } from '@qubitcodes/qcrouter';
import { flatRoutes } from './qcRouter.config';
import { MIDDLEWARE_MAP } from './middleware.config';

const app = express();

app.use((req, res, next) => {
	// Match RegExp & HTTP method
	const matched = Object.values(flatRoutes).find((r) => {
		return r.regex.test(req.path) && (!r.method || r.method === req.method);
	});
	if (!matched) return next();

	const handlers = matched.middlewares.map((name) => MIDDLEWARE_MAP[name]).filter(Boolean);
	return runMiddlewareChain(req, handlers, next);
});

Hono Pipeline Setup

import { Hono } from 'hono';
import { runMiddlewareChain } from '@qubitcodes/qcrouter';
import { flatRoutes } from './qcRouter.config';
import { MIDDLEWARE_MAP } from './middleware.config';

const app = new Hono();

app.use('*', async (c, next) => {
	const matched = Object.values(flatRoutes).find(r => r.regex.test(c.req.path));
	if (!matched) return next();

	const handlers = matched.middlewares.map(name => MIDDLEWARE_MAP[name]).filter(Boolean);
	return runMiddlewareChain(c, handlers, () => next());
});

Astro Middleware Gateway (src/middleware.ts)

import { runMiddlewareChain } from '@qubitcodes/qcrouter';
import { defineMiddleware } from 'astro/middleware';
import { flatRoutes } from '../qcRouter.config';
import { MIDDLEWARE_MAP } from '../middleware.config';

export const onRequest = defineMiddleware((context, next) => {
	const matched = Object.values(flatRoutes).find(r => r.regex.test(context.url.pathname));
	if (!matched) return next();

	const handlers = matched.middlewares.map(name => MIDDLEWARE_MAP[name]).filter(Boolean);
	return runMiddlewareChain(context, handlers, () => next());
});

SvelteKit Hook Gateway (src/hooks.server.ts)

import { runMiddlewareChain } from '@qubitcodes/qcrouter';
import type { Handle } from '@sveltejs/kit';
import { flatRoutes } from '../qcRouter.config';
import { MIDDLEWARE_MAP } from '../middleware.config';

export const handle: Handle = async ({ event, resolve }) => {
	const matched = Object.values(flatRoutes).find(r => r.regex.test(event.url.pathname));
	if (!matched) return resolve(event);

	const handlers = matched.middlewares.map(name => MIDDLEWARE_MAP[name]).filter(Boolean);
	return runMiddlewareChain(event, handlers, () => resolve(event));
};

7. API & Configuration Reference

A. defineRouter(config)

defineRouter is the recommended public API for registering a route config:

import { defineRouter } from '@qubitcodes/qcrouter';

export const qcRouter = defineRouter(qcRouterConfig);
export const { flatRoutes, route } = qcRouter;

It replaces the older manual setup:

const flatRoutes = compileRoutes(qcRouterConfig);
const { route } = createRouter(flatRoutes);

compileRoutes and createRouter remain available for advanced or low-level integrations.

B. Configuration Schema

RouteConfig Properties

The core configuration schema for individual route nodes. | Property | Type | Description | | :--- | :--- | :--- | | path | string (Required) | The URL path pattern. Supports dynamic variables prefixed with : (e.g. /users/:id). | | method | string (Optional) | HTTP request method constraint (e.g. 'GET', 'POST', 'DELETE'). | | middleware | string[] (Optional) | Route-level middlewares to append to the pipeline. | | withoutMiddleware | string[] (Optional) | Group-level middlewares to bypass or exempt from this specific route. |

RouteGroup Properties

The configuration schema for recursive nesting groups. | Property | Type | Description | | :--- | :--- | :--- | | prefix | string (Optional) | Base path prefix appended automatically to all child routes (e.g. /api/v1). | | namePrefix | string (Optional) | Name prefix prepended dynamically to child route names (e.g. 'api.'). | | middleware | string[] (Optional) | Group-level middlewares inherited by all child routes. | | routes | Record<string, string \| RouteConfig \| RouteGroup> (Required) | Child definitions tree containing nested routes or groups. |


C. Type Safety & Parameter Autocomplete

@qubitcodes/qcrouter uses conditional TS types to parse path variables. If a route pattern is:

posts: '/users/:userId/posts/:postId'

Your editor will require both parameters at compile-time:

// Enforces passing required userId and postId:
route('posts', { userId: 'jkthoppil', postId: 123 });

Missing parameters or typing an incorrect route name will trigger a TS compiler error immediately, preventing runtime URL bugs!