REserve: Regex-Configurable HTTP Server
REserve is a lightweight and versatile HTTP server package, currently at stable version 2.3.5. It specializes in serving static files, acting as a reverse proxy, and aggregating resources from multiple sources. The core functionality relies on declarative, regular expression-based mappings defined in a JSON configuration, offering fine-grained control over request routing and handling. The project demonstrates an active release cadence, with frequent minor updates and bug fixes. Its key differentiators include a minimal footprint with zero runtime dependencies, efficient performance (updated to `punycache`@`1.0.1` in v2.3.0), and the power of embedded regular expressions for highly flexible and dynamic server configurations, making it well-suited for development environments and complex routing scenarios without the overhead of larger frameworks.
Common errors
-
Error: listen EADDRINUSE: address already in use :::8080
cause The specified port (e.g., 8080) is already being used by another application or a previously failed server instance.fixChange the `port` in your `reserve` configuration to an unused port, or terminate the process currently occupying the port (e.g., using `lsof -i :8080` and `kill <PID>` on Linux/macOS, or Resource Monitor on Windows). -
404 Not Found (from reserve server)
cause No mapping in the `reserve` configuration matched the incoming request URL, or a `file` handler pointed to a non-existent file.fixReview your `mappings` array. Ensure your `match` regular expressions correctly capture the intended URLs. Check `file` handler paths for correctness relative to the server's working directory or use absolute paths. -
Property 'on' does not exist on type 'void' or 'Promise<void>'
cause Attempting to chain `.on()` calls directly after `serve()` without correctly handling its return type or in a context where `serve` is awaited prematurely.fixEnsure you are calling `.on()` methods directly on the `serve` function's return value, which is an event emitter (Promise-like with `.on()` methods). Do not `await serve(config)` if you intend to chain `.on()` immediately. If using `async/await`, await after chaining events, or capture the returned instance.
Warnings
- breaking The `static` option for `file` handlers now defaults to `false` instead of `true`. If your configuration relied on `static: true` by default, explicitly set it in your mappings.
- gotcha Regular expression matching is fundamental to `reserve` configuration. Incorrect or overly broad regex patterns can lead to unexpected routing, resource exposure, or 404 errors. Carefully test your `match` patterns.
- gotcha In earlier versions, `Server.close()` might not have forcefully closed all pending connections, potentially leading to processes hanging. Version 2.3.0 introduced options to force closing.
Install
-
npm install reserve -
yarn add reserve -
pnpm add reserve
Imports
- serve
const serve = require('reserve')import { serve } from 'reserve' - Configuration
import { Configuration } from 'reserve' - Server
import { Server } from 'reserve'
Quickstart
import { serve, Configuration, Server } from 'reserve';
import * as path from 'path';
const config: Configuration = {
port: 8080,
mappings: [
{
// Serve files from the current working directory
match: '^/(.*)',
file: path.join(process.cwd(), '$1') // Ensures serving from the correct absolute path
},
{
// Default catch-all for unmatched requests
status: 404
}
]
};
let serverInstance: Server | null = null; // To hold the server instance for graceful shutdown
serve(config)
.on('ready', ({ url }: { url: string }) => {
serverInstance = serverInstance; // Capture the instance
console.log(`REserve server running at ${url}`);
console.log(`Try accessing http://localhost:${config.port}/package.json if this file exists in your current directory.`);
})
.on('error', (error: Error) => {
console.error('REserve server encountered an error:', error.message);
})
.on('abort', () => {
console.log('REserve server aborted.');
});
// Implement graceful shutdown on process termination signals
process.on('SIGINT', async () => {
console.log('SIGINT received. Shutting down REserve server...');
if (serverInstance) {
try {
await serverInstance.close();
console.log('REserve server gracefully closed.');
} catch (err) {
console.error('Error closing REserve server:', err);
}
}
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log('SIGTERM received. Shutting down REserve server...');
if (serverInstance) {
try {
await serverInstance.close();
console.log('REserve server gracefully closed.');
} catch (err) {
console.error('Error closing REserve server:', err);
}
}
process.exit(0);
});