Router
The `router` package provides a simple, middleware-style routing solution for Node.js HTTP servers. It is currently at version 2.2.0, with releases occurring periodically, often incorporating dependency updates and internal refactorings to reduce its external footprint, as seen in recent 2.x versions. This module was originally extracted from the Express project, offering a lightweight alternative that can be used directly with Node.js's native `http.createServer` or integrated into other web frameworks. Key differentiators include its minimalistic API, adherence to the familiar `(req, res, next)` middleware signature, and flexible routing capabilities through HTTP method-specific handlers (`router.get`, `router.post`, etc.) and general-purpose middleware (`router.use`). It supports asynchronous middleware, including Promises, and offers options for strictness, case sensitivity, and parameter merging.
Common errors
-
ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client
cause Attempting to set HTTP headers or send response data after a previous middleware or route handler has already completed the response, or called `res.end()`.fixEnsure that only one part of your middleware chain or a single route handler is responsible for explicitly ending the HTTP response. If a middleware doesn't end the response, it must call `next()` without implicitly sending headers. -
Request hangs indefinitely / connection timeout
cause A middleware function or route handler failed to call `next()` to pass control, and also did not explicitly terminate the HTTP response (`res.end()`, `res.json()`, etc.).fixInspect all middleware and route handlers to ensure every execution path either calls `next()` to continue processing or sends a complete HTTP response. -
404 Not Found (from finalhandler)
cause No route defined within the `router` instance matched the incoming request's HTTP method and path, leading the `finalhandler` callback to report a 404.fixVerify that your `router.get()`, `router.post()`, etc., definitions accurately match the expected request paths and HTTP methods. Check for common issues like typos, leading/trailing slashes (controlled by `strict` option), and case sensitivity (controlled by `caseSensitive` option).
Warnings
- breaking The `path-to-regexp` dependency, which powers route matching, underwent significant breaking changes in `v2.0.0-beta.1`. Specifically, the wildcard `(*)` for matching groups is no longer valid and must be written as `(.*)`. Additionally, named matching groups are no longer available by position in `req.params`, requiring explicit naming for parameter access.
- breaking Support for Node.js versions below 0.10 was explicitly dropped in `v2.0.0-alpha.1`. While the current `package.json` specifies `engines.node >= 18`, this historical change marked a significant breaking point for legacy environments.
- gotcha Middleware functions, including those defined with `router.use` or specific HTTP methods, *must* explicitly call `next()` to pass control to the next middleware or handler in the stack. Failing to call `next()` will cause the request to hang indefinitely if the response is not otherwise terminated.
- gotcha The router provides specific control flow options within middleware: `next()`, `next('route')`, and `next('router')`. `next('route')` bypasses remaining middleware and handlers *for the current matching route*, proceeding to the next route in the stack. `next('router')` exits the *current router instance completely*, invoking the top-level callback (e.g., `finalhandler`). Misunderstanding these can lead to unexpected or incorrect routing behavior.
- deprecated The `debug` dependency for internal logging was removed in `v2.0.0-beta.1` but subsequently restored in `v2.2.0`. If you relied on `debug` logging from `router` between these versions (e.g., in `v2.0.x` or `v2.1.x`), it would have been absent.
Install
-
npm install router -
yarn add router -
pnpm add router
Imports
- Router
import { Router } from 'router'; // Router is typically the default export const Router = require('router'); // CommonJS is valid, but ESM is preferred in modern Node.jsimport Router from 'router';
- router(req, res, callback)
router(req, res, finalhandler(req, res));
- router.use
router.use('/api', (req, res, next) => { res.end('Data'); }); // Omits next(), which will stall the request if middleware doesn't end it.router.use('/api', myMiddleware); - finalhandler
const finalhandler = require('finalhandler');import finalhandler from 'finalhandler';
Quickstart
import http from 'node:http';
import Router from 'router';
import finalhandler from 'finalhandler';
const router = Router({
caseSensitive: false, // paths like /hello and /Hello are treated the same
strict: false // trailing slashes are optional
});
// Global middleware to log incoming requests
router.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next(); // Must call next to pass control to subsequent middleware/routes
});
// Handle a GET request to the root path
router.get('/', (req, res) => {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Hello from the Router!');
});
// Handle a POST request to '/data'
router.post('/data', (req, res) => {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', () => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ received: body, status: 'ok' }));
});
});
// Middleware demonstrating next('route') to skip subsequent handlers for the current route
router.get('/skip-example', (req, res, next) => {
if (req.query.skip) {
console.log('Skipping to the next route handler...');
return next('route'); // Skip this handler and the next for this route
}
next();
}, (req, res) => {
res.end('This handler will be skipped if ?skip=true is in the query.');
});
const server = http.createServer((req, res) => {
// The router processes the request; if no route matches or an error occurs,
// finalhandler ensures a proper HTTP response is sent.
router(req, res, finalhandler(req, res, { onerror: console.error }));
});
server.listen(3000, () => {
console.log('Router server listening on http://localhost:3000');
});