Express.js Tutorial: Build a Node Server Step by Step

Express is a small web framework for Node.js. Install it with npm install express, create an app with express(), add routes like app.get('/', handler), and start it with app.listen(3000). Middleware such as express.json() runs before your routes, and an error handler with four arguments goes last.

Express building blocks
NeedCode
Create the appconst app = express()
Handle a GET requestapp.get('/users/:id', (req, res) => res.json({ id: req.params.id }))
Read a JSON bodyapp.use(express.json())
Serve a public folderapp.use(express.static('public'))
Group routes in a fileconst router = express.Router()
Catch errorsapp.use((err, req, res, next) => { ... })

Updated

Most Express tutorials still show version 4 code that breaks or warns on version 5, which is now the default install. This walkthrough uses Express 5 throughout, generates a starter server with the parts you tick, and ends with a table of the changes that trip people up when upgrading an older project.

Set up a project

Install and run

Create a folder, initialise package.json, mark it as an ES module project and install Express.

mkdir my-api && cd my-api
npm init -y
npm pkg set type=module
npm install express
node index.js

Routes in their own file

A router is a mini app. Mount it under a prefix so every path inside is relative to it.

// routes/users.js
import { Router } from "express";
const router = Router();
const users = [{ id: "1", name: "Ana" }];

router.get("/", (req, res) => res.json(users));
router.get("/:id", (req, res) => {
  const user = users.find((u) => u.id === req.params.id);
  if (!user) return res.status(404).json({ error: "Not found" });
  res.json(user);
});
router.post("/", (req, res) => {
  const user = { id: String(users.length + 1), name: req.body?.name };
  users.push(user);
  res.status(201).json(user);
});
export default router;

// index.js
import usersRouter from "./routes/users.js";
app.use("/api/users", usersRouter);

Async errors reach the error handler

In Express 5 a rejected promise in a route is forwarded for you. The four-argument handler, registered last, turns it into a response.

app.get("/boom", async (req, res) => {
  throw new Error("Something failed");
});

app.use((err, req, res, next) => {
  if (res.headersSent) return next(err);
  console.error(err);
  res.status(err.status ?? 500).json({ error: "Server error" });
});

Express 4 to 5: what changed

Express ships codemods for many of these: npx codemod@latest @expressjs/v5-migration-recipe.

Express 4 code and its Express 5 replacement
Express 4Express 5Note
app.get('/*', ...)app.get('/*splat', ...)Wildcards need a name
'/:file.:ext?''/:file{.:ext}'Optional parts use braces
app.del()app.delete()Removed
req.param('id')req.params.id, req.body or req.queryRemoved
res.sendfile()res.sendFile()Removed
res.json(obj, 201)res.status(201).json(obj)Status no longer a second argument
res.redirect('back')res.redirect(req.get('Referrer') || '/')Magic 'back' removed
req.body is {} when unparsedreq.body is undefinedCheck before reading
urlencoded extended: trueextended: false by defaultSet it if you need nested objects
static serves dotfilesdotfiles ignored by defaultSafer default

Routes and parameters

A route is a method, a path and a handler. Named segments such as :id are read from req.params, query strings from req.query, and the parsed body from req.body.

Respond with res.json() for data, res.send() for text or HTML, and res.status(404) chained before either to set the code. Express 5 accepts only whole numbers from 100 to 999 in res.status().

Middleware in one paragraph

Middleware is a function of req, res and next that runs before the route handler. It can read or change the request, end the response, or call next(). Logging, auth checks, CORS headers and body parsing are all middleware, mounted with app.use() for every path or passed to a single route.

How it works

  1. Tick the features you want in the generator: JSON bodies, static files, a router, a 404 page and an error handler.
  2. Choose ES modules or CommonJS to match your package.json.
  3. Copy the file into index.js, run npm install express, then node index.js.
  4. Read the sections below for what each part does and how version 5 differs from 4.

Tips

Frequently asked questions

Which Node.js version does Express 5 need?

Express 5 requires Node.js 18 or newer. If you are stuck on an older Node, stay on Express 4 until you can upgrade the runtime.

Why is req.body undefined?

No body parser ran for that request. Add app.use(express.json()) for JSON or express.urlencoded() for form posts before your routes, and make sure the client sends a matching Content-Type header. In Express 5 an unparsed body is undefined rather than an empty object.

How do I write a catch-all route in Express 5?

Wildcards must be named now. Use app.get('/*splat', handler) instead of '/*'. To match the root path as well, wrap it in braces: '/{*splat}'.

What does next() do?

It hands the request to the next matching middleware or route. Call next() to continue, next(err) to jump to the error handlers, or send a response to stop the chain. Forgetting both leaves the request hanging.

Should I still use body-parser?

Not for JSON and form data. express.json() and express.urlencoded() are built in. Note that in Express 5 the urlencoded parser's extended option defaults to false.

Related: Install Node and npm on a Mac, npm commands cheat sheet, HTTP status codes, CORS explained, Next.js API routes.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap