Express has stayed the default choice for Node.js APIs for over a decade because it does one thing well: it gets out of your way. This walkthrough builds a small API from nothing.
mkdir my-api && cd my-api
npm init -y
npm install express
That's the entire setup. No build step, no config file required to get a server running.
const express = require('express')
const app = express()
app.use(express.json())
app.get('/api/health', (req, res) => {
res.json({ status: 'ok' })
})
app.listen(3000, () => console.log('Listening on 3000'))
express.json() is middleware — a function that runs on every incoming request before your route handler, here parsing the request body into req.body.
let todos = []
app.get('/api/todos', (req, res) => res.json(todos))
app.post('/api/todos', (req, res) => {
const todo = { id: Date.now(), text: req.body.text }
todos.push(todo)
res.status(201).json(todo)
})
app.delete('/api/todos/:id', (req, res) => {
todos = todos.filter(t => t.id !== Number(req.params.id))
res.status(204).end()
})
req.params holds URL segments like :id; req.body holds the parsed JSON payload. Status codes matter — 201 for "created," 204 for "no content to return," and so on.
As routes multiply, group them with express.Router() and mount them under a prefix: app.use('/api/todos', todosRouter). This keeps each file focused on one resource instead of one giant server file.
A real API adds a database (Postgres, MongoDB), input validation, authentication middleware, and a logging library. But the routing and middleware model you've just built is exactly the same shape those additions plug into — nothing here gets thrown away as the app grows.
Comments