Have you ever wondered how mobile apps and websites get their data? The answer is usually an API. In this tutorial, you will build a working REST API from scratch using Node.js and Express. By the end, you will have an API that can create, read, update, and delete data — the four core operations of any real-world application.
No prior API experience needed. If you know basic JavaScript, you are ready.
What You Will Build
A simple Books API that lets you:
- Get a list of all books
- Get a single book by ID
- Add a new book
- Update a book
- Delete a book
Prerequisites
Before starting, make sure you have:
- Node.js installed (version 14 or higher)
- A code editor (VS Code recommended)
- A terminal / command prompt
- Postman or Thunder Client to test your API
Step 1: Set Up Your Project
Open your terminal and run these commands one by one:
# Create a new folder for your project
mkdir books-api
# Move into the folder
cd books-api
# Create a package.json file (just press Enter for all questions)
npm init -y
# Install Express
npm install express
Now create a file called index.js inside your books-api folder. This is where all your code will live.
Your folder structure should look like this:
books-api/
index.js
package.json
node_modules/
Step 2: Create Your First Server
Open index.js and add this code:
// Import Express
const express = require('express');
// Create an Express app
const app = express();
// This line lets our API understand JSON data
app.use(express.json());
// Choose a port number for our server
const PORT = 3000;
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
What each line does:
-
require('express')— loads the Express library we installed -
app.use(express.json())— tells Express to read JSON from incoming requests -
app.listen(PORT)— starts the server and listens for requests on port 3000
Run your server:
node index.js
You should see:
Server is running on http://localhost:3000
Your server is live. Now let's add data and routes.
Step 3: Add Some Sample Data
Since we are not using a database in this tutorial, we will store our books in an array. Add this to your index.js file, just below app.use(express.json()):
// Our "database" — a simple array of books
let books = [
{ id: 1, title: 'The Pragmatic Programmer', author: 'Andrew Hunt' },
{ id: 2, title: 'Clean Code', author: 'Robert C. Martin' },
{ id: 3, title: 'You Don\'t Know JS', author: 'Kyle Simpson' },
];
Think of this array as a temporary database. Every time the server restarts, data resets — but it is perfect for learning.
Step 4: GET All Books
This route returns every book in our list. Add this below your books array:
// GET /books — returns all books
app.get('/books', (req, res) => {
res.json(books);
});
What is happening here:
-
app.get— we are setting up a GET route -
'/books'— the URL path someone visits -
req— the incoming request (what the user sends) -
res— the response (what we send back) -
res.json(books)— sends our books array as JSON
Test it: Open Postman, set the method to GET, and visit http://localhost:3000/books. You will see all three books.
Step 5: GET a Single Book
What if someone wants just one book? We use a URL parameter:
// GET /books/:id — returns one book by ID
app.get('/books/:id', (req, res) => {
// Convert the id from text to a number
const id = parseInt(req.params.id);
// Find the book with that ID
const book = books.find((b) => b.id === id);
// If no book found, send a 404 error
if (!book) {
return res.status(404).json({ message: 'Book not found' });
}
// Send the book back
res.json(book);
});
What is happening here:
-
:idin the URL is a placeholder — it captures whatever number the user types -
req.params.idreads that number from the URL -
parseIntconverts it from a string to a number -
.find()searches the array for a matching book - If nothing is found, we return a 404 status with a helpful message
Test it: GET http://localhost:3000/books/1 returns the first book. GET http://localhost:3000/books/99 returns "Book not found".
Step 6: POST — Add a New Book
Now let's allow adding a new book:
// POST /books — adds a new book
app.post('/books', (req, res) => {
// Get the title and author from the request body
const { title, author } = req.body;
// Simple validation — make sure both fields exist
if (!title || !author) {
return res.status(400).json({ message: 'Title and author are required' });
}
// Create the new book with a unique ID
const newBook = {
id: books.length + 1,
title: title,
author: author,
};
// Add it to our array
books.push(newBook);
// Send back the new book with a 201 status (means "created")
res.status(201).json(newBook);
});
Test it in Postman:
- Method: POST
- URL:
http://localhost:3000/books - Body: raw → JSON
{
"title": "Eloquent JavaScript",
"author": "Marijn Haverbeke"
}
You will get back the new book with an ID of 4.
Step 7: PUT — Update a Book
What if someone made a typo in a book title? This route fixes it:
// PUT /books/:id — updates an existing book
app.put('/books/:id', (req, res) => {
const id = parseInt(req.params.id);
const { title, author } = req.body;
// Find the position of the book in the array
const index = books.findIndex((b) => b.id === id);
// If book doesn't exist, return 404
if (index === -1) {
return res.status(404).json({ message: 'Book not found' });
}
// Update the book — keep the same ID, change title and author
books[index] = { id, title, author };
// Send back the updated book
res.json(books[index]);
});
Test it in Postman:
- Method: PUT
- URL:
http://localhost:3000/books/1 - Body:
{
"title": "The Pragmatic Programmer (20th Anniversary Edition)",
"author": "Andrew Hunt"
}
Step 8: DELETE — Remove a Book
Finally, let's allow deleting a book:
// DELETE /books/:id — removes a book
app.delete('/books/:id', (req, res) => {
const id = parseInt(req.params.id);
// Find the book's position in the array
const index = books.findIndex((b) => b.id === id);
// If not found, return 404
if (index === -1) {
return res.status(404).json({ message: 'Book not found' });
}
// Remove the book from the array
books.splice(index, 1);
// Confirm deletion
res.json({ message: 'Book deleted successfully' });
});
Test it: DELETE http://localhost:3000/books/2 removes Clean Code from the list. Do a GET on /books and confirm it is gone.
Your Complete index.js File
Here is the full code in one place:
const express = require('express');
const app = express();
app.use(express.json());
const PORT = 3000;
let books = [
{ id: 1, title: 'The Pragmatic Programmer', author: 'Andrew Hunt' },
{ id: 2, title: 'Clean Code', author: 'Robert C. Martin' },
{ id: 3, title: "You Don't Know JS", author: 'Kyle Simpson' },
];
// GET all books
app.get('/books', (req, res) => {
res.json(books);
});
// GET one book
app.get('/books/:id', (req, res) => {
const id = parseInt(req.params.id);
const book = books.find((b) => b.id === id);
if (!book) return res.status(404).json({ message: 'Book not found' });
res.json(book);
});
// POST a new book
app.post('/books', (req, res) => {
const { title, author } = req.body;
if (!title || !author) return res.status(400).json({ message: 'Title and author are required' });
const newBook = { id: books.length + 1, title, author };
books.push(newBook);
res.status(201).json(newBook);
});
// PUT update a book
app.put('/books/:id', (req, res) => {
const id = parseInt(req.params.id);
const { title, author } = req.body;
const index = books.findIndex((b) => b.id === id);
if (index === -1) return res.status(404).json({ message: 'Book not found' });
books[index] = { id, title, author };
res.json(books[index]);
});
// DELETE a book
app.delete('/books/:id', (req, res) => {
const id = parseInt(req.params.id);
const index = books.findIndex((b) => b.id === id);
if (index === -1) return res.status(404).json({ message: 'Book not found' });
books.splice(index, 1);
res.json({ message: 'Book deleted successfully' });
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
What You Just Built
You now have a fully working REST API with five routes:
| Method | Route | What it does |
|---|---|---|
| GET | /books | Returns all books |
| GET | /books/:id | Returns one book |
| POST | /books | Adds a new book |
| PUT | /books/:id | Updates a book |
| DELETE | /books/:id | Deletes a book |
What to Learn Next
Now that your API works, here are natural next steps:
- Add a real database — connect MongoDB or PostgreSQL instead of an array
- Add authentication — protect your routes with JWT tokens
- Deploy your API — put it online using Railway or Render (both free)
-
Add input validation — use a library like
express-validatorfor stricter checks
Conclusion
Building a REST API with Node.js and Express is simpler than most people think. You started from an empty folder and ended up with a working API that handles all four CRUD operations. Every API you use daily — Twitter, Instagram, your banking app — works on these same principles at its core.
The code in this tutorial is intentionally simple so you can focus on understanding the concepts. Once these feel comfortable, adding a database and authentication becomes a natural next step.
Happy building.
Top comments (0)