Express is the most used web framework for Node.js. It is small, it has been around for many years and a huge amount of documentation and examples exist for it. Express does not change how Node works. It adds a clean way to describe which code answers which request. In this lesson you will build the same kind of server as in lesson 5, but with far less code.
Install and start
Inside your taskapi folder, you already ran npm install express in lesson 2. If not, run it now. Create a file server.js with this code. This course uses Express 5, which is the current major version. Most of what you learn also applies to Express 4, and we point out the differences where they matter.
import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello from Express');
});
app.get('/api/health', (req, res) => {
res.json({ ok: true, time: new Date().toISOString() });
});
const PORT = 3000;
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
Start it with node server.js, or better, with npm run dev after you have added the scripts from lesson 2. Then test it.
curl http://localhost:3000/
curl http://localhost:3000/api/health
Hello from Express
{"ok":true,"time":"2026-10-28T04:00:00.000Z"}
The time you see will be your current time. Now look at the code in four parts.
express()creates the app, an object that holds your routes and settings.app.get(path, handler)says: when a GET request arrives for this path, run this function. There are matching methodsapp.post,app.patch,app.putandapp.delete.- The handler receives
req(the request) andres(the response). app.listen(port, callback)starts the server. The callback runs once it is ready.
The request object, req
The request object describes what the client sent. You will use these parts all the time.
req.methodandreq.originalUrl: the method and the full URL path with the query string.req.params: values taken from the path, such as the7in/api/tasks/7. Lesson 7 covers this.req.query: values after the question mark, such as?done=true.req.body: the data sent in the body of a POST or PATCH. It needs a body parser, which lesson 8 covers.req.get('Authorization'): reads one request header.req.ip: the address of the client.
The response object, res
You must send exactly one response for each request. If you never send one, the client waits until it times out. If you send two, Node throws an error. These are the methods you will use.
res.send('text')sends text, HTML or a buffer, and sets the content type for you.res.json(object)converts a JavaScript object into JSON and setsContent-Type: application/json. This is the main method for APIs.res.status(404)sets the status code and returnsres, so you can chain:res.status(404).json({ error: 'Not found' }).res.status(204).end()sends an empty response, used after a delete.res.set('Header-Name', 'value')sets a header.res.redirect('/other')sends the client to another URL.
Restart automatically while you work
Node reads your file once at start-up. If you change the code, the old version keeps running until you restart. Instead of stopping and starting by hand, run node --watch server.js. Node watches the files it loaded and restarts whenever one changes. That is what the dev script from lesson 2 does.
Ports and the PORT variable
We used the fixed port 3000. On a real server, you often cannot choose the port yourself, because a hosting service or a process manager gives it to you through an environment variable called PORT. Lesson 11 shows how to read it with a fallback, so the same code runs on your laptop and on a server.
Express 5 handles async errors
In Express 4, if an async handler threw an error or rejected a promise, the error was not caught and you had to wrap every handler by hand. Express 5 catches rejected promises from handlers and passes them to the error handler for you. That makes async handlers much simpler, and you will rely on it from lesson 9.
Common problems
- `Cannot GET /something`: no route matches. Check the path and the method. A POST route does not answer a GET request.
- The browser shows old content: your code did not restart. Use
--watch. - `EADDRINUSE`: the port is already used by another copy of the server. Stop it.
- `Error [ERR_MODULE_NOT_FOUND]`: you forgot
.jsin a file import, or the package is not installed.
Your task
- Create
server.jswith the code above and start it withnode --watch server.js. - Add a route
GET /api/hello/worldthat returns{ "message": "Hello, world" }. - Change the text of the home page while the server runs and refresh. Check that the change appears.
- Use
curl -ion/api/healthand find theContent-Typeheader.
How to check it worked
- The browser shows
Hello from Expressathttp://localhost:3000/. /api/healthreturns JSON withokset totrue.- After saving a change, the terminal shows that the server restarted and the new text appears.