Your First HTTP Server with Plain Node.js

Node.js and Express: Build a Back End API from Scratch · Node.js Basics · 5 min read · Updated

One request, one response

  1. A client, such as a browser, a phone app or curl, wants some data.
  2. It sends a request with a method and a path, for example GET /api/time.
  3. Your server function runs for the request and decides what to answer.
  4. It sends a response with a status code and a body, and the client shows or uses it.

Before using Express, it is worth building one tiny server with only what Node provides. It takes five minutes, and afterwards Express will make sense, because you will see the work that it does for you. In this lesson you will learn how a web request is structured, write a server with the built-in node:http module and test it with curl.

What happens in a web request

HTTP is the conversation between a client, such as a browser, a phone app or curl, and a server. The client sends a request: a method (what to do), a path (which thing), headers (extra information) and sometimes a body (data). The server sends back a response: a status code (how it went), headers and usually a body. Learn these methods first.

  • GET reads data. It should not change anything.
  • POST creates something new, usually with data in the body.
  • PUT and PATCH change something that exists. PUT replaces it, PATCH changes part of it.
  • DELETE removes something.

And these status codes. The first digit tells the story: 2 means success, 3 means redirect, 4 means the client made a mistake, and 5 means the server had a problem.

  • 200 OK: it worked. 201 Created: something new was made. 204 No Content: it worked and there is nothing to send back.
  • 400 Bad Request: the request data is wrong. 401 Unauthorized: log in first. 403 Forbidden: you are logged in but not allowed. 404 Not Found: no such thing.
  • 429 Too Many Requests: slow down. 500 Internal Server Error: the server broke.

A server in a few lines

Create server.js in a new folder, or in taskapi for this lesson.

import http from 'node:http';
const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end('Hello from plain Node');
  } else if (req.method === 'GET' && req.url === '/api/time') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ iso: new Date().toISOString() }));
  } else {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Not found' }));
  }
});
server.listen(3000, () => console.log('Listening on http://localhost:3000'));

http.createServer takes a function that runs for every request. It receives req, which describes the request, and res, which you use to answer. res.writeHead sets the status code and headers, and res.end sends the body and finishes the response. server.listen(3000) starts waiting for connections on port 3000. A port is like a door number on your computer. Run it.

node server.js
Listening on http://localhost:3000

The program does not finish. It keeps running and waiting for requests. Leave it running, open a second terminal, and test it with curl, a command line tool for making requests. Windows 10 and 11, macOS and most Linux systems include it. Or just open the address in a browser.

curl http://localhost:3000/
curl http://localhost:3000/api/time
curl -i http://localhost:3000/nope
Hello from plain Node
{"iso":"2026-10-28T04:00:00.000Z"}
HTTP/1.1 404 Not Found
...
{"error":"Not found"}

The time in your output will be the time on your computer. The -i flag makes curl print the status line and headers as well. Stop the server with Ctrl+C in the first terminal.

Why people use Express

Look at what you had to do by hand. You compared req.method and req.url with an if chain. You set the content type each time. You turned objects into JSON text yourself. For a real API, you would also have to read the request body in pieces, parse the JSON, split the query string from the path, pull out values from URLs such as /api/tasks/7, handle errors in one place and check logins. That is a lot of repeated, easy-to-get-wrong code.

Express is a small, popular library that handles all this for you. It is built directly on node:http, so everything you learned here still applies. In the next lesson you will build the same server in about half the lines.

Do not leave the server open to the world

By default, a server started on your own computer is reachable from your own machine, and on some systems also from the local network, depending on your firewall. While learning, that is fine. Do not put real passwords or private data into a practice server, and never expose a practice server to the internet.

Your task

  1. Create server.js and start it. Test all three URLs with curl or the browser.
  2. Add a route GET /api/hello that returns { "message": "Hello" } as JSON.
  3. Change the port to 3010 and test again.
  4. Use curl -i and find the status code and the Content-Type header of each response.

How to check it worked

  • curl http://localhost:3000/ prints Hello from plain Node.
  • An unknown path returns status 404 and a JSON error.
  • You can name one thing Express would do for you that you wrote by hand here.

Frequently asked questions

What is the difference between 401 and 403?

401 means the client is not logged in or has no valid login. 403 means the client is logged in but is not allowed to do that action.

Do I need Express to build a server?

No. node:http is enough, but you must write much repeated code for routing, JSON bodies and errors. Express removes most of that.