Node.js HTTP Server
A Node.js HTTP server can be created with just the built-in http module, no frameworks required. Understanding it helps you see what frameworks like Express do for you behind the scenes.
Creating a Basic Server
createServer takes a function that runs for every request. req describes the incoming request (method, URL, headers) and res is how you send a response. res.end finishes the response. Stop the server with Ctrl+C.
import http from 'node:http';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from Node.js!');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});Server running at http://localhost:3000 (browser at http://localhost:3000) Hello from Node.js!
Routing by URL and Method
const server = http.createServer((req, res) => {
const { pathname } = new URL(req.url, 'http://localhost');
if (req.method === 'GET' && pathname === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
return res.end('<h1>Home</h1>');
}
if (req.method === 'GET' && pathname === '/api/time') {
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ now: new Date().toISOString() }));
}
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not found');
});GET /api/time
{"now":"2026-10-31T09:15:02.118Z"}
GET /nope
Not foundReading the Request Body
Mark the handler async to use await. In production you must also catch invalid JSON and limit body size.
async function readJson(req) {
const chunks = [];
for await (const chunk of req) chunks.push(chunk);
return JSON.parse(Buffer.concat(chunks).toString() || '{}');
}
// inside the request handler
if (req.method === 'POST' && pathname === '/api/echo') {
const body = await readJson(req);
res.writeHead(201, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify({ received: body }));
}POST /api/echo {"name":"Hina"}
{"received":{"name":"Hina"}}Making HTTP Requests with fetch
Node 18+ includes the same fetch function as browsers, so calling other APIs is easy.
const res = await fetch('https://api.github.com/users/octocat');
const user = await res.json();
console.log(user.name, '-', user.public_repos, 'repos');The Octocat - 8 repos
Common HTTP Status Codes
- 200 OK, 201 Created, 204 No Content
- 301/302 Redirect, 304 Not Modified
- 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found
- 500 Internal Server Error
Why Use a Framework?
The raw http module works, but routing, body parsing, cookies and error handling quickly become repetitive. Express adds these features with a tiny, readable API, which is why the next chapters switch to it.
Frequently asked questions
How do I create a web server in Node.js without Express?
Import node:http, call http.createServer with a (req, res) handler, and call server.listen(port). Use res.writeHead and res.end to send responses.
How do I return JSON from a Node.js server?
Set the Content-Type header to application/json and send JSON.stringify(data) with res.end.
Does Node.js have fetch built in?
Yes. Since Node 18, fetch is available globally without installing node-fetch or axios.