Node.js CommonJS + ESM · HTTP + HTTPS
Own the listener lifecycle.
Create isolated HTTP, HTTPS-only, or paired servers, inspect active Node listeners, serve custom responses, and extend the request lifecycle with hooks.
Imports
Use the singleton or create instances.
| Module style | Default | Named exports |
|---|---|---|
| CommonJS | require('node-http-server') | Server, Config, RefString |
| Node.js ESM | import server from 'node-http-server' | Server, Config, RefString |
const {Server}=require('node-http-server');
const server=new Server({root:'./public'});
server.deploy();import {Server} from 'node-http-server';
const server=new Server({root:'./public'});
server.deploy();new Server() when code may own more than one listener or needs independent configuration.Lifecycle
Deploy, observe readiness, close, redeploy.
import {Server} from 'node-http-server';
const server=new Server({root:'./public',port:8080});
server.deploy((instance,nodeServer)=>{
console.log(nodeServer.address());
});
process.once('SIGTERM',async()=>{
await server.close();
});| Step | Contract |
|---|---|
new Server(config?) | Creates isolated configuration and listener state. |
deploy(config?, callback?) | Overlays current config, starts HTTP and optional HTTPS, returns the same instance immediately, and calls the callback once as each listener becomes ready. A function first argument is accepted as the callback. |
close(callback?) | Returns a Promise that resolves after every owned listener closes. HTTP/2 sessions close gracefully after their active streams finish. Concurrent calls share it; idle close is safe; callback receives no arguments or an error. |
| Redeploy | The same instance may deploy again after close() resolves. |
ERR_SERVER_ALREADY_DEPLOYED. Await close() before reusing it. Omitted settings persist across later deployment overlays.Public members
Methods and state.
| Member | Returns | Use |
|---|---|---|
deploy(config?, callback?) | Server | Start the instance's listeners. |
close(callback?) | Promise<void> | Close every listener owned by the instance. |
address() | address object or null | Read the first active listener address. |
serve(request,response,body='',encoding='utf8') | Promise | Complete a deliberate response through hooks; defaults to text/plain, honors HEAD and Content-Length. |
serveFile(filename,request,response) | Promise<boolean> | Serve a deliberate trusted file; legacy (filename,exists,request,response) remains accepted. |
config | Config | Read the instance's isolated active configuration. |
server | Node HTTP server or null | Access the active HTTP listener. |
secureServer | Node Http2SecureServer, https.Server, or null | Access the active HTTPS listener. HTTP/2 with HTTP/1.1 negotiation is the default; https.http2:false selects the original https.Server. |
lastError | Error or null | Last captured request, hook, stream, HTTP/2 session, or logging failure. |
Parsed request data
Use text or original bytes.
| Member | Type | Value |
|---|---|---|
request.body | string | UTF-8 request body. |
request.bodyBuffer | Buffer | Original request bytes. |
request.uri | object | Parsed URL and query information. |
request.url | string | Processed request path. |
request.serverRoot | string | Selected static root after Host routing. |
When maxRequestBodyBytes is set and the request crosses it, parsing stops with 413 Payload Too Large.
HTTP/2 request bodies are read through the request stream even without Content-Length, and the complete body forms reach onRequest. Virtual-host routing uses HTTP/2 :authority or HTTP/1 Host values to select request.serverRoot.
Hooks
Take over only the step you need.
Hooks receive native Node request/response objects for the negotiated protocol, including Node's HTTP/2 compatibility objects when HTTPS selects HTTP/2. Use public methods such as response.setHeader() and response.end(), or the supplied serve continuation. HTTP/2 streams cannot use raw HTTP/1 socket writes or connection-specific headers such as Connection and Transfer-Encoding. request.httpVersionMajor identifies the request protocol. See Node's compatibility API for protocol-specific behavior.
| Hook | Arguments | When | Takeover |
|---|---|---|---|
onRawRequest | request,response,serve | Immediately after receipt, before body parsing. | Return truthy; supplied serve is the public safe serve path. |
onRequest | request,response,serve | After URL helpers and body forms are ready. | Return truthy; supplied serve is the public safe serve path. |
beforeServe | request,response,bodyRef,encodingRef,complete | Before a buffered response is sent. | Return truthy; complete is one-shot and proceeds directly to response completion. |
afterServe | request,response | After the response finishes. | Observe; may return a Promise. |
import {Server} from 'node-http-server';
class AppServer extends Server{
async onRequest(request,response,serve){
if(request.url!='/health') return false;
response.setHeader('Content-Type','application/json');
await serve(request,response,JSON.stringify({ok:true}));
return true;
}
}
new AppServer({root:'./public'}).deploy();Rewrite a buffered text file safely
server.beforeServe=function(request,response,bodyRef){
if(!Buffer.isBuffer(bodyRef.value)) return false;
const text=bodyRef.value.toString('utf8');
bodyRef.value=Buffer.from(text.replaceAll('{{mode}}','preview'));
return false;
};beforeServe buffers every static file and bypasses normal streaming/automatic compression. Static file bodies arrive as Buffers; convert explicitly before string replacement. RefString remains exported for this compatibility shape.afterServe runs for library completion paths. A hook that calls response.end() directly bypasses it. Use and await the supplied serve function when completion observation matters.Manual serving
Respond from a hook without replacing the server.
Call and await the supplied safe serve continuation for generated content. Use serveFile() only when custom routing selects a deliberate trusted file. Both preserve the server's response/error lifecycle.
| Need | Call | Expected result |
|---|---|---|
| Generated text/JSON | await serve(request,response,body,'utf8') | Completes through beforeServe/afterServe. |
| Selected trusted file | await this.serveFile(filename,request,response) | Returns whether a file was served. |
| Low-level Node behavior | response.end(...) | Your hook owns headers/body and bypasses library completion hooks; return truthy to stop fallback. |
serveFile() accepts the caller-provided filename verbatim and deliberately bypasses automatic dotfile routing policy. Resolve and validate application-selected files before passing them to it.Error contract
Listener errors remain native Node events.
const {Server}=require('node-http-server');
const server=new Server({root:'./public'});
server.deploy();
server.server.once('error',error=>{
console.error(error);
});lastError captures request, hook, stream, HTTP/2 session, and logging failures. Node listener failures arrive through the listener's error event. Attach handlers to both server.server and server.secureServer when both protocols run. Synchronous deployment validation—invalid roots, ports, timeout values, or incomplete HTTPS configuration—throws before a usable listener is ready.