Run a simple web server with Node.js
Serve an HTTP response with Node.js, add a small route, or preview a static directory with a dedicated server.
Node.js includes an HTTP server API, so a small local server needs no framework. For a directory of HTML, CSS, and JavaScript files, a static-server package is usually more convenient.
Start with the built-in HTTP module
Save this as server.cjs and run node server.cjs:
const http = require('node:http');
const server = http.createServer((req, res) => {
let pathname;
try {
pathname = new URL(req.url, 'http://localhost').pathname;
} catch {
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Bad request\n');
return;
}
if (req.method === 'GET' && pathname === '/') {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Hello from Node.js!\n');
} else if (req.method === 'GET' && pathname === '/api/time') {
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({ time: new Date().toISOString() }));
} else {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Not found\n');
}
});
server.on('error', (error) => {
console.error(error);
process.exitCode = 1;
});
server.listen(3000, '127.0.0.1', () => {
console.log('Open http://127.0.0.1:3000');
});
req describes the incoming request, while res writes the response. Every branch calls res.end() to finish its response. Parsing the pathname makes /api/time?format=json reach the same route as /api/time.
The explicit loopback address makes this server available locally. Binding to 0.0.0.0 exposes it on IPv4 interfaces, subject to network controls. Omitting the host may bind an unspecified IPv6 or IPv4 address; it is not a guarantee of localhost-only access.
Preview static files
From a project containing a public directory:
npx http-server ./public -a 127.0.0.1 -p 8080
Only put files intended for serving in that directory. Starting a static server at the repository root can expose source files or configuration. npx can download and execute a package, so use a package and version you trust; a pinned development dependency is useful for repeatable team workflows.
serve is another static-server option. Review its listening and routing options when using it for a single-page application. Neither command runs arbitrary backend routes from your application's source automatically.
Troubleshooting and deployment
Why does the port fail? EADDRINUSE usually means another listener already occupies the requested address and port. Identify that process, stop it normally if appropriate, or select another port.
Can this serve HTTPS? Use node:https with a key and certificate, or terminate TLS at a configured proxy. For local HTTPS, a locally trusted development certificate avoids treating a production certificate setup as a prerequisite.
Is the example a production service? It illustrates routing and response handling. A deployed service also needs request limits, error handling, controlled exposure, lifecycle management, and suitable TLS configuration. Express or another framework can supply routing and middleware, while a proxy may handle TLS and static delivery.
See Node HTTP and the http-server options.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan