Section 1: Node.js and Express basics
Your first Express 5 server
Create a project folder and initialise it:
mkdir recipebox && cd recipebox
npm init -y
npm install express
In package.json, add "type": "module" so we can use import syntax, and a dev script:
"scripts": {
"dev": "node --watch --env-file=.env src/server.js"
}
A minimal app
Create src/app.js:
import express from "express";
export const app = express();
app.use(express.json({ limit: "100kb" }));
app.get("/health", (req, res) => {
res.json({ status: "ok" });
});
And src/server.js:
import { app } from "./app.js";
const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => {
console.log(`Recipebox API listening on http://localhost:${port}`);
});
Separating the app from the code that starts the server lets tests import the app without opening a port.
Run npm run dev and open http://localhost:3000/health. Edit the response and save: --watch restarts the server automatically.
What express.json does
express.json() is middleware that reads the request body, parses JSON and puts the result on req.body. The limit option rejects bodies larger than 100 KB, a simple protection against clients sending huge payloads. Without this middleware, req.body is undefined.
Route handlers
A handler receives req (the request) and res (the response):
req.paramsholds path parameters, likeidin/recipes/:id.req.queryholds query string values.req.bodyholds the parsed body.res.status(201).json(data)sets the status and sends JSON.
Let us add a temporary in memory list:
const recipes = [];
app.post("/recipes", (req, res) => {
const recipe = { id: crypto.randomUUID(), ...req.body };
recipes.push(recipe);
res.status(201).json(recipe);
});
app.get("/recipes", (req, res) => {
res.json(recipes);
});
Test with curl or an HTTP client such as the REST Client extension in VS Code:
curl -X POST localhost:3000/recipes -H "Content-Type: application/json" -d '{"title":"Lentil soup"}'
Notice the problem: we accept any body at all, including an empty title or extra fields. We fix that with validation shortly. First, we organise the code with routers.