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

@chaeco/route-wizard

v0.0.7

Published

File-based automatic route registration for Node.js frameworks

Readme

@chaeco/route-wizard

NPM version Build Status Test Coverage Codecov Bundle Size Dependencies License Node.js Version TypeScript GitHub issues GitHub pull requests Last commit

File-based automatic route registration for Node.js frameworks with zero configuration. Stop manually registering routes and let your file structure do the work!

✨ Features

  • 📁 File-driven routing: Automatically generate routes from file structure with zero configuration
  • 🏗️ Multi-framework support: Extensible design supporting Express, Koa, Hoa.js, Hono, Fastify, and NestJS
  • 📝 Full TypeScript support: Complete type definitions for better development experience
  • High performance: Synchronous scanning with zero runtime overhead
  • 🧩 Framework agnostic: Works with any Node.js framework that supports route registration
  • 🎯 Convention over configuration: Sensible defaults with extensive customization options
  • 🛡️ Production ready: Comprehensive test coverage and battle-tested in production
  • 🔄 Dynamic parameters: Support for nested and optional parameters

🚀 Installation

npm install @chaeco/route-wizard

🎯 Quick Start

1. Create controller files

Create route files in the controllers directory:

// controllers/users/get.ts
export default async (req, res) => {
  const users = await db.users.findMany();
  res.json(users);
};

// controllers/users/post.ts
export default async (req, res) => {
  const user = await db.users.create({ data: req.body });
  res.json(user);
};

// controllers/users/[id]/get.ts
export default async (req, res) => {
  const user = await db.users.findUnique({
    where: { id: req.params.id },
  });
  res.json(user);
};

2. Register routes

import express from 'express';
import { registerRoutes } from '@chaeco/route-wizard';

const app = express();
app.use(express.json());

// Register routes - that's it!
registerRoutes(app, {
  dir: './controllers',
  prefix: '/api', // optional
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

3. Your routes are ready

GET    /api/users          # List all users
POST   /api/users          # Create user
GET    /api/users/:id      # Get user by ID
PUT    /api/users/:id      # Update user
DELETE /api/users/:id      # Delete user

📁 File Convention

Route-wizard uses a clean, intuitive file structure with support for both folder-based and filename-based routing:

Folder-based (legacy)

controllers/
├── users/
│   ├── get.ts              # GET /users
│   ├── post.ts             # POST /users
│   └── [id]/
│       ├── get.ts          # GET /users/:id
│       ├── put.ts          # PUT /users/:id
│       └── delete.ts       # DELETE /users/:id
├── users/
│   └── [userId]/
│       └── posts/
│           ├── get.ts              # GET /users/:userId/posts
│           └── [postId]/
│               └── get.ts          # GET /users/:userId/posts/:postId
└── search/
    └── [[query]]/
        └── get.ts                  # GET /search/:query?

Filename-based (recommended for deep nesting)

controllers/
├── users.get.ts            # GET /users
├── users.post.ts           # POST /users
├── users.[id].get.ts       # GET /users/:id
├── users.[id].put.ts       # PUT /users/:id
├── users.[id].delete.ts    # DELETE /users/:id
├── users.[userId].posts.get.ts          # GET /users/:userId/posts
├── users.[userId].posts.[postId].get.ts # GET /users/:userId/posts/:postId
└── search.[[query]].get.ts               # GET /search/:query?

Parameter Types

  • [param]:param (required parameter)
  • [[param]]:param? (optional parameter)

Module Format Support

Route-wizard supports both ES6 and CommonJS module formats:

ES6 Format (recommended):

// controllers/users/get.ts
export default async (req, res) => {
  const users = await db.users.findMany();
  res.json(users);
};

CommonJS Format:

// controllers/users/get.js
module.exports = async (req, res) => {
  const users = await db.users.findMany();
  res.json(users);
};

Both formats are fully supported and can be mixed in the same project.

🔧 Advanced Usage

Middleware Support

Route Wizard supports per-route middleware configuration. You can attach middleware to routes that will be executed before the handler:

// controllers/users/get.ts
export default {
  handler: async (req, res) => {
    const users = await db.users.findMany();
    res.json(users);
  },
  middlewares: [authenticate, authorize('admin')],
};

Or with folder-based routing:

// controllers/api/users/get.ts
const authenticate = (req, res, next) => {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
};

const handler = async (req, res) => {
  const users = await db.users.findMany();
  res.json(users);
};

export default { handler, middlewares: [authenticate] };

Performance Monitoring

Monitor your route scanning and request performance:

import express from 'express';
import { registerRoutes, PerformanceMonitor, createRouteWizard } from '@chaeco/route-wizard';

const app = express();

// Option 1: Pass existing monitor
const monitor = new PerformanceMonitor();
registerRoutes(app, {
  dir: './controllers',
  performanceMonitor: monitor,
});

// Get metrics
const metrics = monitor.getMetrics();
console.log(`Route scan time: ${metrics.routeScanTime}ms`);
console.log(`Total requests: ${metrics.totalRequests}`);
console.log(`Cache hit rate: ${(metrics.cacheHitRate * 100).toFixed(1)}%`);

// Get summary
console.log(monitor.getMetricsSummary());

// Option 2: Use route wizard with built-in monitoring
const wizard = createRouteWizard({
  dir: './controllers',
  enableMonitoring: true,
});

wizard.register(app);

// Access metrics from wizard
const metrics = wizard.getMetrics();
console.log(metrics?.routeScanTime);

PerformanceMonitor API

  • recordRouteScan(duration: number): Record a route scanning operation
  • recordRequest(responseTime: number): Record a request with response time
  • recordCacheHit(): Record a successful cache hit
  • recordCacheMiss(): Record a cache miss
  • getMetrics(): Get current performance metrics
  • getMetricsSummary(): Get a formatted summary string
  • reset(): Reset all metrics

Custom Separator

You can customize the separator used in filename-based routing:

registerRoutes(app, {
  dir: './controllers',
  separator: '_', // Use underscore instead of dot
});

With underscore separator:

controllers/
├── api_users.get.ts        # GET /api/users
├── users_[id].get.ts       # GET /users/:id
└── users_[id]_posts.get.ts # GET /users/:id/posts

Depth Limiting

Limit the maximum route depth to prevent overly complex URLs:

registerRoutes(app, {
  dir: './controllers',
  maxDepth: 3, // Maximum 3 path segments
});

Routes exceeding the depth limit will be ignored.

Multiple Parameters

// controllers/users/[userId]/posts/[postId]/get.ts
export default async (req, res) => {
  const { userId, postId } = req.params;
  const post = await db.posts.findFirst({
    where: {
      id: parseInt(postId),
      userId: parseInt(userId),
    },
  });
  res.json({ post });
};

Optional Parameters

// controllers/search/[[query]]/get.ts
export default async (req, res) => {
  const { query } = req.params;

  if (query) {
    const results = await searchDatabase(query);
    res.json({ query, results });
  } else {
    res.json({ message: 'Search endpoint - add query param' });
  }
};

Framework Support

Route-wizard supports multiple frameworks:

// Express
import express from 'express';
import { registerRoutes } from '@chaeco/route-wizard';

const app = express();
registerRoutes(app, { dir: './controllers' });

// Koa
import Koa from 'koa';
import { registerRoutes } from '@chaeco/route-wizard';

const app = new Koa();
registerRoutes(app, { dir: './controllers' });

// Fastify
import fastify from 'fastify';
import { registerRoutes } from '@chaeco/route-wizard';

const app = fastify();
registerRoutes(app, { dir: './controllers' });

📚 API Reference

registerRoutes(app, options)

Register routes to your application.

Parameters

  • app: Framework app instance (Express, Koa, Fastify, etc.)
  • options: Configuration options

Options

  • dir (string): Path to controllers directory (default: './controllers')
  • prefix (string): Route prefix (default: '')
  • logEnabled (boolean): Enable logging (default: true)
  • separator (string): Separator for filename-based routing (default: '.')
  • maxDepth (number): Maximum depth for nested routes (optional)
  • performanceMonitor (PerformanceMonitor): Optional performance monitor instance

Example

registerRoutes(app, {
  dir: './routes',
  prefix: '/api',
  logEnabled: false,
});

createRouteWizard(options)

Create a route wizard with optional performance monitoring.

Parameters

  • options: Configuration options

Options

  • dir (string): Path to controllers directory
  • prefix (string): Route prefix (optional)
  • logEnabled (boolean): Enable logging (default: true)
  • separator (string): Separator for filename-based routing (default: '.')
  • maxDepth (number): Maximum depth for nested routes (optional)
  • enableMonitoring (boolean): Enable performance monitoring (default: false)

Returns

Object with the following methods:

  • register(app): Register routes to the app
  • getMetrics(): Get performance metrics
  • getSummary(): Get formatted metrics summary

Example

const wizard = createRouteWizard({
  dir: './controllers',
  enableMonitoring: true,
});

wizard.register(app);
console.log(wizard.getMetrics());
console.log(wizard.getSummary());

PerformanceMonitor

Monitor performance metrics for route operations.

Methods

  • recordRouteScan(duration: number): Record route scan duration
  • recordRequest(responseTime: number): Record request response time
  • recordCacheHit(): Record cache hit
  • recordCacheMiss(): Record cache miss
  • getMetrics(): Get current metrics
  • getMetricsSummary(): Get formatted summary
  • reset(): Reset all metrics

Metrics Properties

  • routeScanTime (number): Average route scan time in milliseconds
  • cacheHitRate (number): Cache hit rate (0-1)
  • totalRequests (number): Total number of requests
  • averageResponseTime (number): Average response time in milliseconds
  • memoryUsage: Node.js memory usage information
  • uptime (number): Monitor uptime in milliseconds

Example

const monitor = new PerformanceMonitor();
monitor.recordRouteScan(50);
monitor.recordRequest(100);
monitor.recordCacheHit();

const metrics = monitor.getMetrics();
console.log(metrics.routeScanTime);
console.log(monitor.getMetricsSummary());

scanRoutes(dir, options?)

Scan and return all routes from a directory.

Parameters

  • dir (string): Path to controllers directory
  • options (ScanOptions): Scan options

Options

  • separator (string): Separator for filename-based routing (default: '.')
  • maxDepth (number): Maximum depth for nested routes (optional)

Returns

Array of Route objects with:

  • method (string): HTTP method (GET, POST, etc.)
  • path (string): Route path
  • handler (function): Route handler
  • middlewares (function[]): Optional middleware array

Example

import { scanRoutes } from '@chaeco/route-wizard';

const routes = scanRoutes('./controllers');
routes.forEach(route => {
  console.log(`${route.method} ${route.path}`);
});

📄 License

MIT © chaeco

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

# Clone the repository
git clone https://github.com/chaeco/route-wizard.git
cd route-wizard

# Install dependencies
npm install

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Build the project
npm run build

# Run linting
npm run lint