Practical examples of basic HTTP server examples in Node.js

If you’re learning Node.js, nothing beats seeing real code. In this guide, we’ll walk through practical examples of basic HTTP server examples in Node.js that you can actually copy, run, and tweak. Instead of abstract theory, you’ll see how to spin up servers for static files, JSON APIs, simple routing, and more. These examples of basic HTTP server examples in Node.js are intentionally minimal but realistic. They mirror the kind of servers you’d build for coding interviews, prototypes, internal tools, and teaching demos. Along the way, we’ll talk about 2024–2025 best practices: using modern JavaScript syntax, thinking about security from day one, and understanding when to move from the built‑in `http` module to something like Express. By the end, you’ll have several working scripts plus a mental model for how Node’s HTTP layer actually behaves under the hood—no magic, just event‑driven JavaScript wired straight to the network stack.
Written by
Jamie
Published

Let’s start with the most stripped‑down example of basic HTTP server examples in Node.js: a single file that returns plain text to any request.

// hello-world-server.js
const http = require('http');

const PORT = process.env.PORT || 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain; charset=utf-8');
  res.end('Hello, world from Node.js HTTP server!');
});

server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

Run it with:

node hello-world-server.js

This is the canonical example of how Node’s core http module works: you create a server, handle every request in a callback, and explicitly write headers and body. Many other examples of basic HTTP server examples in Node.js are just this pattern with extra logic layered on.


Examples of basic HTTP server examples in Node.js for JSON APIs

Text is boring. Most real examples include JSON. Here’s a tiny JSON API that returns data and respects HTTP methods.

// json-api-server.js
const http = require('http');

const PORT = process.env.PORT || 3001;

const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json; charset=utf-8');

  if (req.method === 'GET' && req.url === '/status') {
    const payload = {
      status: 'ok',
      timestamp: new Date().toISOString(),
      nodeVersion: process.version
    };
    res.writeHead(200);
    return res.end(JSON.stringify(payload));
  }

  if (req.method === 'POST' && req.url === '/echo') {
    let body = '';

    req.on('data', chunk => {
      body += chunk;
      // Basic protection: avoid giant bodies in 2024+ environments
      if (body.length > 1e6) req.socket.destroy();
    });

    req.on('end', () => {
      res.writeHead(200);
      res.end(JSON.stringify({ received: body }));
    });

    return;
  }

  res.writeHead(404);
  res.end(JSON.stringify({ error: 'Not found' }));
});

server.listen(PORT, () => {
  console.log(`JSON API server at http://localhost:${PORT}/`);
});

This is one of the best examples of how a basic HTTP server can double as a teaching tool for streaming request bodies and handling different methods.


Serving static files: real examples of a tiny Node.js file server

A lot of tutorials skip over the fact that static files are still everywhere. You can build a tiny static file server with just the http, fs, and path modules.

// static-file-server.js
const http = require('http');
const fs = require('fs');
const path = require('path');

const PORT = process.env.PORT || 3002;
const PUBLIC_DIR = path.join(__dirname, 'public');

const MIME_TYPES = {
  '.html': 'text/html; charset=utf-8',
  '.css': 'text/css; charset=utf-8',
  '.js': 'application/javascript; charset=utf-8',
  '.json': 'application/json; charset=utf-8',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.gif': 'image/gif'
};

const server = http.createServer((req, res) => {
  const safePath = path.normalize(req.url.split('?')[0]).replace(/^\/+/, '');
  const filePath = path.join(PUBLIC_DIR, safePath || 'index.html');

  // Prevent directory traversal
  if (!filePath.startsWith(PUBLIC_DIR)) {
    res.writeHead(403);
    return res.end('Forbidden');
  }

  fs.stat(filePath, (err, stats) => {
    if (err || !stats.isFile()) {
      res.writeHead(404);
      return res.end('Not found');
    }

    const ext = path.extname(filePath).toLowerCase();
    const contentType = MIME_TYPES[ext] || 'application/octet-stream';

    res.writeHead(200, { 'Content-Type': contentType });

    const readStream = fs.createReadStream(filePath);
    readStream.pipe(res);
  });
});

server.listen(PORT, () => {
  console.log(`Static file server at http://localhost:${PORT}/`);
});

Among the examples of basic HTTP server examples in Node.js, this one is closest to what you’d deploy behind a reverse proxy like Nginx in 2024 for quick internal tools or documentation previews.


Manual routing: examples include path‑based handlers without Express

You don’t have to install Express to get routing. Here’s a small router built directly on the http module.

// router-server.js
const http = require('http');

const PORT = process.env.PORT || 3003;

function sendJson(res, statusCode, payload) {
  res.writeHead(statusCode, { 'Content-Type': 'application/json; charset=utf-8' });
  res.end(JSON.stringify(payload));
}

const server = http.createServer((req, res) => {
  const { method, url } = req;

  if (method === 'GET' && url === '/') {
    return sendJson(res, 200, { message: 'Home route' });
  }

  if (method === 'GET' && url === '/users') {
    return sendJson(res, 200, { users: ['alice', 'bob'] });
  }

  if (method === 'POST' && url === '/users') {
    let body = '';
    req.on('data', chunk => (body += chunk));
    req.on('end', () => {
      sendJson(res, 201, { created: body || null });
    });
    return;
  }

  sendJson(res, 404, { error: 'Route not found' });
});

server.listen(PORT, () => {
  console.log(`Router server at http://localhost:${PORT}/`);
});

This is a realistic example of basic HTTP server examples in Node.js when you want full control and zero dependencies, which still matters for security‑sensitive environments and interview settings.


HTTPS example of a basic Node.js server with TLS

In 2024–2025, plain HTTP is increasingly frowned upon for anything beyond local development. Here’s a minimal HTTPS server using self‑signed certificates.

// https-server.js
const https = require('https');
const fs = require('fs');

const PORT = process.env.PORT || 3443;

const options = {
  key: fs.readFileSync('./certs/server.key'),
  cert: fs.readFileSync('./certs/server.crt')
};

const server = https.createServer(options, (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.end('Secure HTTPS server in Node.js');
});

server.listen(PORT, () => {
  console.log(`HTTPS server at https://localhost:${PORT}/`);
});

You’d normally pair this with something like Let’s Encrypt in production, but as an example of basic HTTP server examples in Node.js upgraded to HTTPS, it illustrates how little code is required to bring TLS into the picture.

For current guidance on HTTPS and encryption, the U.S. National Institute of Standards and Technology maintains up‑to‑date recommendations on TLS configurations and cryptography: https://csrc.nist.gov.


Streaming and large responses: real examples using Node’s strengths

One reason Node.js remains popular in 2025 is its event‑driven I/O model. Here’s a server that streams a large text response instead of buffering it all in memory.

// streaming-server.js
const http = require('http');

const PORT = process.env.PORT || 3004;

const server = http.createServer((req, res) => {
  if (req.url === '/stream') {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });

    let count = 0;
    const interval = setInterval(() => {
      count += 1;
      res.write(`Chunk ${count}\n`);

      if (count >= 5) {
        clearInterval(interval);
        res.end('Done streaming.');
      }
    }, 1000);

    return;
  }

  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.end('Hit /stream to see streaming in action');
});

server.listen(PORT, () => {
  console.log(`Streaming server at http://localhost:${PORT}/`);
});

This is one of the best examples of basic HTTP server examples in Node.js that shows why Node is so good at long‑lived connections, server‑sent events, and log streaming dashboards.


Mixing HTTP and WebSockets: an example of real‑time basics

Modern apps often need a mix of normal HTTP and WebSocket connections. Here’s a compact example using the popular ws package.

// websocket-mixed-server.js
const http = require('http');
const WebSocket = require('ws');

const PORT = process.env.PORT || 3005;

const server = http.createServer((req, res) => {
  if (req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    return res.end('WebSocket server is running. Connect via WS client.');
  }

  res.writeHead(404);
  res.end('Not found');
});

const wss = new WebSocket.Server({ server });

wss.on('connection', ws => {
  ws.send('Welcome to the WebSocket server');

  ws.on('message', message => {
    ws.send(`You said: ${message}`);
  });
});

server.listen(PORT, () => {
  console.log(`HTTP+WS server at http://localhost:${PORT}/`);
});

While this goes slightly beyond the absolute bare minimum, it’s still in the family of examples of basic HTTP server examples in Node.js, and it reflects the kind of hybrid setups teams actually use for dashboards, chat, and live updates.


Using modern syntax: examples of basic HTTP server examples in Node.js with async/await

Node 18+ (LTS in 2024–2025) supports modern JavaScript comfortably. Here’s an example that uses async/await and fs.promises for cleaner code.

// async-await-server.mjs
import http from 'http';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const PORT = process.env.PORT || 3006;

const server = http.createServer(async (req, res) => {
  try {
    if (req.method === 'GET' && req.url === '/config') {
      const filePath = path.join(__dirname, 'config.json');
      const fileContent = await fs.readFile(filePath, 'utf8');

      res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
      return res.end(fileContent);
    }

    res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
    res.end(JSON.stringify({ error: 'Not found' }));
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
    res.end(JSON.stringify({ error: 'Server error' }));
  }
});

server.listen(PORT, () => {
  console.log(`Async/await server at http://localhost:${PORT}/`);
});

If you’re teaching or learning modern JavaScript, this is a clean example of basic HTTP server examples in Node.js that still feels aligned with how production codebases are written in 2025.


When to move from raw HTTP to frameworks in 2025

All these examples of basic HTTP server examples in Node.js are built on the core http (and https) modules. That’s intentional: you should understand the foundation before adding layers.

But in real‑world projects, you’ll probably switch to a framework when:

  • You need middleware, authentication, and validation.
  • You’re handling dozens of routes.
  • You want built‑in routing, param parsing, and cookies.

Express, Koa, Fastify, and NestJS all sit on top of the same primitives you’ve just seen. The mental model you build from these basic examples carries over, even if you never touch http.createServer directly again.

If you want a more academic perspective on networked systems and concurrency—the stuff Node leans on—the open courseware from MIT (https://ocw.mit.edu) and Stanford (https://online.stanford.edu) have solid background material on distributed systems and operating systems that pairs nicely with hands‑on Node work.


FAQ: examples of basic HTTP server examples in Node.js

Q: What are some real examples of basic HTTP server examples in Node.js I can use for practice?
The scripts above cover most starter needs: a hello‑world server, a JSON API server, a static file server, a manual router, an HTTPS server, a streaming server, a mixed HTTP+WebSocket server, and an async/await‑based file reader. Together, these examples include almost every pattern you’ll hit in early‑stage projects.

Q: Which example of a Node.js HTTP server should I learn first?
Start with the hello‑world text server, then jump straight to the JSON API and static file servers. Those three examples of basic HTTP server examples in Node.js give you a solid understanding of status codes, headers, methods, and response formats.

Q: Are these basic HTTP server examples production‑ready?
They’re intentionally minimal. For production you’d harden them: stricter input validation, logging, rate limiting, environment‑based configuration, and better error handling. Still, many teams use very similar patterns for internal tools, prototypes, and teaching environments.

Q: How do these examples compare to using Express?
Express handles routing, middleware, and a lot of boilerplate for you. The best examples of Express servers hide the raw http layer, but underneath it’s the same evented request/response cycle you see in these basic HTTP server examples in Node.js.

Q: Do I need HTTPS for local development with these examples?
Not strictly, but it’s worth understanding the HTTPS example of a Node.js server so you’re comfortable with certificates and TLS. Many modern APIs and browsers expect HTTPS even in staging environments, and security guidance from organizations like NIST (https://csrc.nist.gov) strongly encourages encrypted traffic for anything sensitive.

Explore More Node.js Code Snippets

Discover more examples and insights in this category.

View All Node.js Code Snippets