Building Your First REST API with Node.js and Express

A beginner-friendly walkthrough of building a real REST API with Node.js and Express in 2026. Learn routing, request handling, JSON responses, error handling, and how to test your endpoints with curl and Postman.

Node.jsbeginner
13 min read

A REST API is just a web server that speaks JSON over HTTP. Once you can build one, the entire backend half of the web opens up — your front end can fetch data, your mobile app can sync, and other services can integrate with what you built. The fastest, friendliest path in the Node.js ecosystem is still Express, which shipped its long-awaited 5.0 stable release in October 2024 and is the framework most tutorials, jobs, and Stack Overflow answers assume in 2026.

This guide walks through building a real REST API for a tiny "books" resource — list, get, create, update, delete — using Express 5. No database, no auth (yet); just the routing, middleware, and request-handling foundations that every Node backend builds on.

What "REST API" Actually Means

REST is a convention for designing HTTP APIs around resources. A resource is a noun (books, users, orders) and the HTTP method is the verb you apply to it.

MethodPathWhat it does
GET/booksList all books
GET/books/:idGet one book
POST/booksCreate a book
PUT/books/:idReplace a book
PATCH/books/:idUpdate some fields
DELETE/books/:idDelete a book

Status codes carry meaning: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found, 500 Internal Server Error. Stick to the conventions and any client (browser, mobile, another service) can consume your API without surprises.

Setting Up the Project

You need Node.js 22 LTS or newer. In a fresh folder:

bashbash
mkdir books-api && cd books-api
npm init -y
npm pkg set type=module
npm install express

Setting "type": "module" lets you use modern import/export instead of require. Open the folder in VS Code and create server.js.

The Minimal Express Server

index.jsindex.js
// server.js
import express from "express";
 
const app = express();
app.use(express.json());
 
app.get("/", (_req, res) => {
  res.json({ status: "ok" });
});
 
app.listen(3000, () => console.log("API on http://localhost:3000"));

Run node server.js and hit http://localhost:3000 in your browser. You should see {"status":"ok"}. That is a real HTTP server in nine lines.

Two things to notice. app.use(express.json()) is middleware — it runs on every request and parses JSON bodies into req.body. Without it, POST requests arrive empty. Express 5 made async middleware fully supported, so you can await inside route handlers without any extra wrapping.

Building the Books Resource

We will keep books in memory for now. Replace your file with this:

index.jsindex.js
import express from "express";
import { randomUUID } from "node:crypto";
 
const app = express();
app.use(express.json());
 
const books = new Map();
 
// list
app.get("/books", (_req, res) => {
  res.json([...books.values()]);
});
 
// get one
app.get("/books/:id", (req, res) => {
  const book = books.get(req.params.id);
  if (!book) return res.status(404).json({ error: "Not found" });
  res.json(book);
});

The pattern is consistent: pick a method (get, post, put, delete), give it a path, and a handler that gets req and res. The path can include :params that show up on req.params. Query strings (?q=node) come in as req.query. JSON bodies come in as req.body because of the JSON middleware.

Create, Update, Delete

Add the rest of the CRUD operations below the read handlers:

index.jsindex.js
app.post("/books", (req, res) => {
  const { title, author } = req.body ?? {};
  if (!title || !author) {
    return res.status(400).json({ error: "title and author are required" });
  }
  const book = { id: randomUUID(), title, author };
  books.set(book.id, book);
  res.status(201).json(book);
});
 
app.delete("/books/:id", (req, res) => {
  if (!books.delete(req.params.id)) {
    return res.status(404).json({ error: "Not found" });
  }
  res.status(204).end();
});
 
app.listen(3000, () => console.log("API on http://localhost:3000"));

Test it from a second terminal with curl:

bashbash
curl -X POST http://localhost:3000/books \
  -H "content-type: application/json" \
  -d '{"title":"Dune","author":"Frank Herbert"}'
 
curl http://localhost:3000/books

You now have a working REST API. The same shape — middleware, routes, handlers that read req and write res — scales to a full production app.

Middleware: The Real Power of Express

Middleware are functions that run between the request arriving and your handler responding. They share one signature: (req, res, next). Call next() to pass control to the next middleware.

index.jsindex.js
app.use((req, _res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

That is a request logger. Real apps stack many: cors() for CORS, helmet() for security headers, an auth middleware that verifies a JWT and attaches req.user, a rate limiter, an error handler at the very end. Express 5 supports async middleware that throws — errors are forwarded to error handlers automatically, no try/catch wrapper needed.

What Comes Next

In a real project the in-memory Map is replaced by a database (Postgres with Drizzle or Prisma, or MongoDB), and you split routes into separate files using express.Router(). Add validation with Zod, authentication with JWTs or sessions, and tests with Vitest. Each piece slots into the same middleware-and-handler model you already built.

For pure Node.js fundamentals before you go deeper, see What is Node.js?.

Common Mistakes Beginners Make

  • Forgetting app.use(express.json()) and being confused why req.body is undefined.
  • Returning the wrong status code (200 for a created resource — should be 201).
  • Putting business logic directly in route handlers. Extract it into functions you can test in isolation.
  • Letting errors crash the server. Add an error-handling middleware: app.use((err, req, res, next) => { ... }).
  • Hard-coding the port. Use process.env.PORT ?? 3000 so deployment platforms can override it.

Quick Reference

  • Install: npm install express
  • Module mode: npm pkg set type=module
  • Routes: app.get, app.post, app.put, app.patch, app.delete
  • Path params: :idreq.params.id. Query: req.query. Body: req.body
  • JSON body parser: app.use(express.json())
  • Status codes: 200, 201 created, 204 no content, 400, 404, 500
  • Middleware shape: (req, res, next) => { ... next(); }
  • Group routes: const r = express.Router(); then app.use("/books", r);
  • Hot reload in dev: node --watch server.js (built into Node 18+)
Rune AI

Rune AI

Key Insights

  • REST means HTTP methods (GET, POST, PUT, PATCH, DELETE) applied to resource URLs that return JSON.
  • Express 5 (stable since 2024) is the default beginner-friendly framework for Node.js APIs in 2026.
  • app.use(express.json()) is required to parse JSON request bodies.
  • Use the right status codes: 200, 201, 204, 400, 404, 500 — clients depend on them.
  • Middleware is the extension point: logging, auth, validation, rate limiting all plug in the same way.
RunePowered by Rune AI

Frequently Asked Questions

Express vs Fastify vs Hono in 2026?

Express is the safest, most-documented, default choice. Fastify is faster and more opinionated. Hono is the modern minimalist favourite for edge runtimes. Learn Express first; the other two are quick switches once you understand the model.

Do I need TypeScript?

Not on day one. Add it once your API has more than a few resources. Express has solid types via `@types/express`.

Where do I store the data?

Start with Postgres (managed by Supabase, Neon, or Railway). It is the default 2026 choice. MongoDB is fine for document-shaped data. SQLite via [Drizzle](https://orm.drizzle.team/) is great for small apps and local dev.

How do I deploy this?

Render, Railway, Fly.io, or any VPS. For full-stack apps with both UI and API, deploying [Next.js](/tutorials/web-development/nextjs/what-is-nextjs-a-beginners-guide-to-the-react-framework-in-2026) on Vercel covers both halves.

Is Express still maintained?

Yes — actively. Express 5.0 stable shipped in October 2024 after a long beta and gets regular releases.

Conclusion

Express turns the raw node:http module into something you actually want to use. Routes, middleware, and handlers are the only three concepts you need on day one, and the same shape carries you all the way to a production backend. Build the books API, swap the Map for Postgres, add a users resource and a JWT middleware, and you have a real backend.