Building a REST API with Node.js and Express

intermediate
15 min

Building a REST API with Node.js and Express

Time to build. Express is the most widely used Node.js web framework: minimal, unopinionated, and backed by fifteen years of ecosystem. In this lesson we set up a project from scratch and implement a books API with proper structure, middleware, and error handling, the skeleton you can reuse for any service.

Project Setup

With Node.js 18+ installed:

bash
mkdir books-api && cd books-api npm init -y npm install express npm install --save-dev nodemon

Add scripts to package.json so npm run dev restarts the server on every file change:

json
"scripts": { "start": "node server.js", "dev": "nodemon server.js" }

Understanding Middleware

Everything in Express is middleware: functions with the signature (req, res, next) that run in order for each request. Each one can inspect or modify the request, end the response, or call next() to pass control along. Routes are just middleware that usually end the chain.

javascript
app.use(express.json()); // parse JSON bodies onto req.body app.use((req, res, next) => { // tiny request logger console.log(new Date().toISOString(), req.method, req.url); next(); });

This pipeline model is why Express stays small: logging, auth, CORS, and rate limiting are all just middleware you compose in order. Order matters; a logger registered after your routes never sees them.

Routes and Route Parameters

Route paths can contain named parameters, exposed on req.params. Query strings appear on req.query and parsed bodies on req.body.

javascript
app.get('/api/books/:id', (req, res) => { const book = db.find(b => b.id === Number(req.params.id)); if (!book) return res.status(404).json({ error: 'book not found' }); res.json(book); });

As an app grows, group related routes with express.Router into their own files (routes/books.js) and mount them with app.use('/api/books', booksRouter). This keeps each file focused on one resource, mirroring the resource-oriented URL design from earlier lessons.

Centralized Error Handling

Express recognizes error-handling middleware by its four-argument signature. Register one after all routes, and route handlers can simply throw or call next(err) instead of formatting errors in twenty places.

javascript
app.use((err, req, res, next) => { console.error(err); const status = err.status || 500; res.status(status).json({ error: { code: err.code || 'internal_error', message: status === 500 ? 'Something went wrong' : err.message } }); });

Note the 500 branch hides internal details from callers while logging the full error server-side, a security habit we will revisit in the security lesson.

The Shape of a Production App

Our sample keeps data in an array so it runs anywhere, but the structure is production-shaped: a server.js entry point, routers per resource, middleware for cross-cutting concerns, and one error handler. From here, real projects typically add a database layer (Prisma, Mongoose, or a query builder), a validation library like Zod on every request body, environment-based config via process.env, and helmet plus cors middleware. Each drops into the same skeleton without restructuring.

Running and Testing

Start with npm run dev, then exercise it exactly as the previous lesson taught: curl the happy path, send invalid bodies to confirm 400s, request missing IDs to confirm 404s. If you saved a Postman collection for the CRUD design lesson, point its {{baseUrl}} at http://localhost:3000 and run the whole lifecycle.

Key Takeaways

  • Express apps are ordered middleware pipelines; routes are middleware that end the chain.
  • express.json() parses bodies; routers group each resource's endpoints in one file.
  • A single four-argument error handler centralizes error responses and logging.
  • The array-backed skeleton here scales to production by swapping in a database and validation layer.

Next up: API Authentication with API Keys, our first layer of access control.

Building a REST API with Node.js and Express - REST APIs | CodeYourCraft | CodeYourCraft