Build a REST API with Node.js and Express
Build your first REST API with Node.js and Express in about 50 lines: routes, JSON, POST, PUT and DELETE, tested with curl, plus where to go next.

Every app you use talks to a server somewhere. When your phone shows a list of movies or your to-do app saves a task, it is sending a request to an API and getting data back. Building one yourself is one of the most satisfying early wins in web development, because you go from "I write code" to "my code answers other programs."
In this guide you will build a small REST API with Node.js and Express: a bookshelf you can list, read, add to, update and delete from. It is about 50 lines, and you can finish it in an hour.
New to REST? Read How REST APIs work, explained for beginners first. It explains requests, responses and status codes without any code, and this guide puts them to work.
What you need first
You should be comfortable with variables, functions, arrays and objects in JavaScript. If you are still deciding which language to start with, Python vs JavaScript: which should you learn first? will help you choose. There is nothing to install: the code editor, the running server and a tool for sending requests are all in your browser on start.dev.
What a REST API actually is
An API is a set of URLs that return data instead of web pages. REST is a convention for how those URLs behave:
- The path names the thing you want, like
/api/booksor/api/books/2. - The method says what to do with it:
GETreads,POSTcreates,PUTupdates,DELETEremoves. - The status code tells the client what happened:
200for success,201for created,404for not found.
That is the whole idea. If the vocabulary feels fuzzy, the lessons Clients And Servers (opens in a new tab) and Methods And Paths (opens in a new tab) explain it with live requests, and Status Codes (opens in a new tab) covers the codes you will use most.
Step 1: Start your server
Express is the library that does the heavy lifting, and it is already available in the course workspace. (On your own machine you would add it with npm install express.) Start with a server that answers one route:
import express from "express"
const app = express()
app.use(express.json())
app.get("/", (req, res) => {
res.send("Hello from the Bookshelf API")
})
app.listen(3000, () => {
console.log("Server running on port 3000")
})Run it, then use the HTTP Client to send a GET request to /. You should see your message come back with a 200 status. That is a working server.
The course builds this up gradually. Your First Dependency (opens in a new tab) shows what a dependency really is, and Hello Express (opens in a new tab) walks through this exact starting point. The line app.use(express.json()) teaches Express to read JSON request bodies, which you will need in step 3.
Step 2: Read data with GET
Real APIs use a database. To keep things simple, we will store books in an array. Add this above app.listen:
let books = [
{ id: 1, title: "Dune", author: "Frank Herbert" },
{ id: 2, title: "Neuromancer", author: "William Gibson" },
]
let nextId = 3
app.get("/api/books", (req, res) => {
res.json(books)
})
app.get("/api/books/:id", (req, res) => {
const book = books.find((b) => b.id === Number(req.params.id))
if (!book) {
return res.status(404).json({ error: "Book not found" })
}
res.json(book)
})The :id in the path is a route parameter. Express puts whatever the client sends into req.params.id, and you use it to find the right book. If nothing matches, you return a 404 instead of pretending everything is fine.
To go deeper, Thinking In Resources (opens in a new tab) explains how to design your paths, Route Params (opens in a new tab) covers params, and When The Id Doesnt Exist (opens in a new tab) is all about handling missing items gracefully.
Send a GET to /api/books/1 to see Dune, then try /api/books/99 to see your 404 in action.
Step 3: Create data with POST
Reading is easy because a plain GET is enough. Creating needs a request body: the data you want to send. First add the route:
app.post("/api/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: nextId++, title, author }
books.push(book)
res.status(201).json(book)
})Then, in the HTTP Client, switch the method dropdown from GET to POST, set the path to /api/books, and put this JSON in the Body:
{ "title": "Hyperion", "author": "Dan Simmons" }Click Send. You get the new book back with a 201 Created status. Now remove the title and send again: you get a 400 with a helpful message. That check is called validation, and it is the difference between an API that works on your machine and one that survives real users.
The matching lessons are Reading The Body (opens in a new tab), The Create Route (opens in a new tab) and When The Body Is Wrong (opens in a new tab).
Step 4: Update and delete
The last two routes complete the set, often called CRUD (create, read, update, delete):
app.put("/api/books/:id", (req, res) => {
const book = books.find((b) => b.id === Number(req.params.id))
if (!book) {
return res.status(404).json({ error: "Book not found" })
}
const { title, author } = req.body
if (!title || !author) {
return res.status(400).json({ error: "title and author are required" })
}
book.title = title
book.author = author
res.json(book)
})
app.delete("/api/books/:id", (req, res) => {
const index = books.findIndex((b) => b.id === Number(req.params.id))
if (index === -1) {
return res.status(404).json({ error: "Book not found" })
}
books.splice(index, 1)
res.status(204).end()
})A successful delete returns 204 No Content: it worked, and there is nothing to send back. See Updating With Put (opens in a new tab) and Delete And 204 (opens in a new tab) for the reasoning behind each choice.
Try them out with the HTTP Client:
PUTto/api/books/1with the body{ "title": "Dune Messiah", "author": "Frank Herbert" }, thenGET /api/books/1to see the change.DELETEto/api/books/2, thenGET /api/booksto confirm it is gone.
Congratulations: that is a complete REST API. Every serious backend you will meet later follows the same pattern, with more routes and a real database behind it. When you reach the database part, SQL for beginners: the 10 queries to learn first is a good place to start.
Common beginner mistakes
- Forgetting
express.json(). Without it,req.bodyisundefinedand your POST route fails in confusing ways. - Testing old code. If a request behaves as though your change is not there, make sure the server picked up your latest code before you send it again.
- Comparing a string to a number. Route params are always strings, which is why the code above wraps them in
Number(). - Always returning
200. Status codes are part of your API's contract. Use201,400and404honestly. - Putting everything in one file forever. It is fine for a first API, but you will want to split things up soon.
What to build next
This API forgets everything when you stop the server, and anyone can call it. The next steps fix both:
- Add middleware for logging, API-key checks and error handling. Read Express Middleware Explained for Beginners for the big picture, then build it yourself, starting with The Pipeline And Next (opens in a new tab).
- Organize your code with routers and controllers, starting at The Router (opens in a new tab).
- Save data to disk so it survives restarts, beginning with Reading From A File (opens in a new tab).
You can also practice what you just learned on a real project: Project Bookshelf API (opens in a new tab) has you build this same API with guided checks along the way.
Keep going
You now have the shape of every backend: requests come in, your code decides what to do, and JSON goes out. The full course, Building APIs with Node.js & Express (opens in a new tab), takes you from here to a structured, persistent API through hands-on projects, all in the browser.
If any of the JavaScript in this post felt shaky, shore it up with the courses below first. A little more fluency with arrays, objects and functions makes everything on the server side easier.









