zorvix
v2.0.1
Published
A static http/https file server
Maintainers
Readme
Zorvix
A fast zero-dependency Node.js typed http/1.1 server supporting CLI with TLS, in-built clustering, caching, and an API that adds REST routing and Express style middleware support.
Full Benchmarks against Express and 0http
CLI
npx zorvix <port> [options]
nix run github:DanielLMcGuire/Zorvix -- -- <port> [options]| Flag | Description |
| --- | --- |
| <port> | Port to listen on (required) |
| -r, --root <dir> | Directory to serve (default: cwd) |
| -l, --log | Enable request logging |
| --dev | Single process, no cache, exit on uncaught exception |
| -dt, --devtools | Enable Chrome DevTools workspace |
| --key / --cert | PEM key and cert files to enable HTTPS |
npx zorvix 8080 # Serve current directory
npx zorvix 3000 --root ./dist -l # Serve ./dist with logging
npx zorvix 443 --key ./key.pem --cert ./cert.pem # HTTPS
npx zorvix 8080 --dev --devtools -l # Dev mode + DevToolsFull zorvix(1) documentation
Examples
# Serve the current directory on port 8080
npx zorvix 8080
# Serve a specific directory with request logging
npx zorvix 3000 --root ./dist -l
# HTTPS
npx zorvix 443 --key ./key.pem --cert ./cert.pem
# Dev mode with DevTools workspace
npx zorvix 8080 --dev --devtools -lAPI
createServer(options)
Creates and returns a ServerInstance. Use this for single-process servers, tests, and any case where you don't need workers.
import { createServer } from 'zorvix';
const server = createServer({
port: 3000,
root: './public',
logging: true,
});
server.get('/hello', (req, res) => res.html('<h1>Hello, world!</h1>'));
await server.start();
await server.stop();serve(options, setup)
Use serve as the cluster-safe application entrypoint. The behavior of the setup argument depends on whether workers mode is enabled.
Cluster Mode (workers: true)setup must be a string representing the absolute path to a module. The primary process forks a supervised worker that dynamically imports this module.
import { serve } from 'zorvix';
import { fileURLToPath } from 'url';
serve(
{ port: 3000, workers: true, logging: true },
fileURLToPath(new URL('./app.js', import.meta.url))
);
export default async function(server) {
await db.connect();
server.get('/users/:id', async (req, res) => {
res.json(await db.findUser(req.params.id));
});
await server.start();
}Single-Process Mode (workers: false)setup must be a callback function.
import { serve } from 'zorvix';
serve({ port: 3000 }, async (server) => {
server.get('/healthz', (req, res) => res.json({ ok: true }));
await server.start();
});Routes and middleware
Both createServer and serve return/provide a ServerInstance with the same API:
server.use((req, res, next) => { next(); });
server.use('/api', authMiddleware);
server.get('/users/:id', (req, res) => res.json({ id: req.params.id }));
server.post('/users', handler);
server.put('/users/:id', handler);
server.patch('/users/:id', handler);
server.delete('/users/:id', handler);
server.head('/users/:id', handler);
server.options('/users', handler);
await server.start();
await server.stop();Body Parsing
Zorvix provides an opt-in createBodyParser middleware to populate req.body.
import { createServer, createBodyParser } from 'zorvix';
const server = createServer({ port: 3000 });
server.use(createBodyParser());
server.post('/users', async (req, res) => {
// req.body is available after using the middleware
const user = await db.createUser(req.body);
res.json(user, 201);
});
await server.start();Full zorvix(3) documentation
Features
- Caching -
ETag(SHA-1 for cached files, mtime-based for streamed) andLast-Modifiedheaders;304 Not ModifiedforIf-None-Match/If-Modified-Since - Range requests -
Accept-Ranges: bytesadvertised;RangeandIf-Rangesupported; multi-range (multipart/byteranges) supported - Gzip - compressible MIME types are compressed when the client sends
Accept-Encoding: gzip - Content-Disposition - binary/archive types are served with
attachmentso browsers download rather than render - TLS - pass
key/certas file paths or pre-loadedBuffers to switch tohttps.createServer - Clustering -
serve()guarantees user code never runs in the primary; primary supervises a worker and restarts it after 500 ms on unexpected exit; clean signal exits are not restarted - Graceful shutdown - idle connections closed immediately; active connections given a 5 s grace period before forced termination
- DevTools - serves
/.well-known/appspecific/com.chrome.devtools.jsonwith a per-process UUID to register the root as a Chrome DevTools workspace - WebSocket-friendly -
server.serverexposes the underlyinghttp.Server/https.Serverfor attaching WebSocket libraries - Query params -
req.querypopulated on every request; repeated keys collapse tostring[], single-occurrence keys remainstring - Body parsing - opt-in
createBodyParser()middleware; supportsapplication/jsonandapplication/x-www-form-urlencoded; configurable byte limit (default 1 MiB); populatesreq.body - Response helpers -
res.json(data, status?)andres.html(markup, status?)set correctContent-TypeandContent-Lengthautomatically - TypeScript - typings generated and bundled
- npm - no runtime npm dependencies; requires Node.js 22+
- Security - path traversal →
400; URLs > 2048 bytes →414; > 50 headers →431; unmatched non-GET/HEAD →405with accurateAllowheader
