English | Русский
HTTP static file router for Node.js.
The module is useful for embedding documentation or administrative panels directly into a server application, avoiding the deployment of additional infrastructure.
- Integration into an existing HTTP server.
- Managing file system access via routes.
- Using streams to save RAM.
npm install @e22m4u/js-http-static-routerThe module supports ESM and CommonJS standards.
ESM
import {HttpStaticRouter} from '@e22m4u/js-http-static-router';CommonJS
const {HttpStaticRouter} = require('@e22m4u/js-http-static-router');The example assumes the following project structure.
/static
├── index.html
└── /assets
└── rabbit.txt
/src
└── server.jsRouter instantiation, route definition, and server startup.
// src/server.js
import path from 'path';
import http from 'http';
import {HttpStaticRouter} from '@e22m4u/js-http-static-router';
// instantiating the router
const staticRouter = new HttpStaticRouter({
// when using the "baseDir" option, relative paths
// in registered routes will be resolved relative to
// the specified file system path
baseDir: path.join(import.meta.dirname, '../static'),
});
// access to import.meta.dirname (current module directory)
// is available only for ESM starting from Node.js 20.11.0
// serving the "index.html" file
// as an index page
staticRouter.defineRoute({
remotePath: '/',
resourcePath: './index.html',
});
// serving the "assets" directory content
// for access relative to the root
// example: http://localhost:3000/rabbit.txt
staticRouter.defineRoute({
remotePath: '/',
resourcePath: './assets',
});
// creating an HTTP server and defining
// a listener for request handling
const server = new http.Server();
server.on('request', async (req, res) => {
const fileSent = await staticRouter.handleRequest(req, res);
// if the file was not sent,
// 404 Not Found is returned
if (!fileSent) {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.write('404 Not Found');
res.end();
}
});
// starting the server
server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
console.log('Try to open:');
console.log('http://localhost:3000');
console.log('http://localhost:3000/rabbit.txt');
});Node.js process startup.
node ./src/server.jsThe HttpStaticRouter class is the main module component. It is responsible
for storing route definitions, matching incoming HTTP requests with file system
resources, and streaming data to the client.
The router constructor accepts an options object that allows setting basic operation parameters.
Signature:
type HttpStaticRouterOptions = {
baseDir?: string;
};
constructor(options?: HttpStaticRouterOptions);Parameters
baseDir: string- absolute path to the base directory.
If the parameter is specified, all relative paths during route registration will be resolved relative to this directory.
Example
Instantiation with the specified base directory.
import path from 'path';
import {HttpStaticRouter} from '@e22m4u/js-http-static-router';
// router creation with specifying the absolute path
// to the directory where static files are stored
const staticRouter = new HttpStaticRouter({
baseDir: path.join(import.meta.dirname, '../public'),
});i. Access to the import.meta.dirname variable (current module directory)
is possible only when using the ESM standard, starting from Node.js 20.11.0.
For earlier versions or CommonJS, __dirname is used.
The defineRoute method adds a new routing rule, linking a virtual path
(URL) with an actual file or directory on the server. When calling this
method, the router checks the physical existence of the specified resource
in the file system.
Signature:
type StaticRouteDefinition = {
remotePath: string;
resourcePath: string;
};
defineRoute(routeDef: StaticRouteDefinition): StaticRoute;Parameters
-
remotePath: string
URL prefix that an incoming request must start with (must start with a slash/). -
resourcePath: string
Path to an existing file or directory in the file system.
i. If the baseDir option was not specified during router
instantiation, the resourcePath parameter value must
be an absolute path.
Examples
Specific file registration. A request to the specified path in the remotePath
parameter will return the associated file content. The router takes into account
the presence/absence of a trailing slash at the end of the path.
// it is assumed that the router was created
// with the specified base directory ("baseDir" option)
staticRouter.defineRoute({
remotePath: '/about',
resourcePath: './pages/about.html',
});
// GET /about
// -> returns the ./pages/about.html contentDirectory content serving. If the route points to a directory, an additional part of the URL will be automatically appended to the file system path.
// granting access to all files
// inside the "assets" directory
staticRouter.defineRoute({
remotePath: '/public',
resourcePath: './assets',
});
// GET /public/images/logo.png
// -> returns the ./assets/images/logo.png contentRoute registration using an absolute path.
import path from 'path';
import {HttpStaticRouter} from '@e22m4u/js-http-static-router';
// instantiation without parameters
const router = new HttpStaticRouter();
// since the "baseDir" option is not set, the router requires
// an absolute path for the "resourcePath" property,
// otherwise an InvalidArgumentError will be thrown
router.defineRoute({
remotePath: '/robots.txt',
resourcePath: path.join(import.meta.dirname, '../public/robots.txt'),
});i. The router performs a security check. If a client attempts to traverse
outside the directory using relative paths during a request (for example,
GET /public/../../config.json), the handler will interrupt the search and the
file will not be sent.
The handleRequest method performs matching of an incoming HTTP request with
registered routes and sends the found file to the client. When reading a file,
the router uses streams, allowing safe delivery of large files without
overloading the server's RAM.
The router automatically determines the file MIME-type based on its extension, sets the necessary headers, and correctly handles connection drops from the client side, closing the file stream in a timely manner to prevent memory leaks.
Signature:
handleRequest(
request: IncomingMessage,
response: ServerResponse,
): Promise<boolean>;Parameters
request: IncomingMessage- native Node.js incoming request stream;response: ServerResponse- native Node.js outgoing response stream;
Return value
The method returns a Promise that resolves to a boolean false if the route
does not match, the target file is physically missing, or the request method is
not supported. In all other cases, the value will be true, which allows
determining whether the router has taken responsibility for handling the
request.
Example
Method integration into a native HTTP server event handler. If handleRequest
returns false, the server takes responsibility for sending an error response
to the client.
import http from 'http';
import {HttpStaticRouter} from '@e22m4u/js-http-static-router';
// instantiating the router
const staticRouter = new HttpStaticRouter();
// staticRouter.defineRoute(...)
const server = new http.Server();
server.on('request', async (req, res) => {
// passing request and response objects to the router
const fileSent = await staticRouter.handleRequest(req, res);
// if the router returned false, the file was not sent
if (!fileSent) {
// manual sending of 404 Not Found status
res.writeHead(404, {'Content-Type': 'text/plain; charset=utf-8'});
res.write('404 Not Found');
res.end();
}
});The router handles exclusively requests with GET and HEAD methods. Upon
receiving a request with any other method, processing is interrupted and the
method returns false. In the case of a HEAD request, the router correctly
calculates the file size and sends the appropriate headers, skipping the
response body transmission.
npm run testMIT