Building a REST API is a fundamental skill for any backend or full-stack developer. It's the backbone of modern web applications, allowing the frontend to communicate with the server to fetch and manipulate data. Node.js and the Express framework are the most popular choices for building fast, scalable, and lightweight APIs with JavaScript.
This comprehensive tutorial will guide you step-by-step through building a complete REST API from scratch. We will cover all four essential **CRUD** operations:
- **C**reate: Add new data.
- **R**ead: Retrieve data.
- **U**pdate: Modify existing data.
- **D**elete: Remove data.
To keep this guide focused on the API itself, we'll use a simple in-memory array as our "database." By the end, you'll have a solid understanding of how to build and structure a RESTful API with Node.js and Express.
Prerequisites
- Node.js installed on your computer.
- A code editor, such as VS Code.
- An API testing tool like Postman to test our endpoints.
Step 1: Project Setup and Installing Dependencies
First, let's create a new directory for our project and initialize it with npm.
mkdir node-express-api
cd node-express-api
npm init -y
This creates a `package.json` file. Now, let's install Express, the web framework we'll be using.
npm install express
For a better development experience, we'll also install `nodemon`. This tool automatically restarts our server whenever we save a file. We'll install it as a development dependency.
npm install nodemon --save-dev
Next, open `package.json` and add a `dev` script to run our server with nodemon:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon index.js"
},
Finally, create the main file for our application: `index.js`.
Step 2: Creating a Basic Express Server
Let's write the initial code to get a simple Express server up and running. Open `index.js` and add the following:
const express = require('express');
const app = express();
const port = 3000;
// Middleware to parse JSON bodies
app.use(express.json());
// A simple root route to test the server
app.get('/', (req, res) => {
res.send('Hello, World! Your API is running.');
});
app.listen(port, () => {
console.log(`Server is listening at http://localhost:${port}`);
});
We import Express, create an app instance, and tell it to listen on port 3000. The `express.json()` middleware is crucial—it allows our server to read and parse JSON data sent in request bodies.
Run the server from your terminal:
npm run dev
If you navigate to `http://localhost:3000` in your browser, you should see the "Hello, World!" message.
Step 3: Setting Up Routes and In-Memory Data
Let's create an in-memory "database" and set up the routes for our API. We'll manage a collection of books.
Add this data at the top of your `index.js` file, right after the `require` statements:
let books = [
{ id: 1, title: 'The Hobbit', author: 'J.R.R. Tolkien' },
{ id: 2, title: '1984', author: 'George Orwell' },
{ id: 3, title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' }
];
Step 4: Implementing CRUD Endpoints
Now for the core of our API. We'll add endpoints for each CRUD operation.
GET /api/books - Read All Books
This endpoint will return the entire list of books.
// GET all books
app.get('/api/books', (req, res) => {
res.json(books);
});
GET /api/books/:id - Read a Single Book
This endpoint uses a URL parameter (`:id`) to find and return a specific book.
// GET a single book by ID
app.get('/api/books/:id', (req, res) => {
const bookId = parseInt(req.params.id);
const book = books.find(b => b.id === bookId);
if (!book) {
return res.status(404).send('The book with the given ID was not found.');
}
res.json(book);
});
POST /api/books - Create a New Book
This endpoint will add a new book to our collection from the data in the request body.
// POST a new book
app.post('/api/books', (req, res) => {
// Basic validation
if (!req.body.title || !req.body.author) {
return res.status(400).send('Title and author are required.');
}
const newBook = {
id: books.length + 1, // Simple ID generation
title: req.body.title,
author: req.body.author
};
books.push(newBook);
res.status(201).json(newBook);
});
PUT /api/books/:id - Update an Existing Book
This endpoint finds a book by its ID and updates its information.
// PUT (update) an existing book
app.put('/api/books/:id', (req, res) => {
const bookId = parseInt(req.params.id);
const book = books.find(b => b.id === bookId);
if (!book) {
return res.status(404).send('The book with the given ID was not found.');
}
// Update book properties
book.title = req.body.title || book.title;
book.author = req.body.author || book.author;
res.json(book);
});
DELETE /api/books/:id - Delete a Book
Finally, this endpoint removes a book from our array.
// DELETE a book
app.delete('/api/books/:id', (req, res) => {
const bookId = parseInt(req.params.id);
const bookIndex = books.findIndex(b => b.id === bookId);
if (bookIndex === -1) {
return res.status(404).send('The book with the given ID was not found.');
}
// Remove the book from the array
const deletedBook = books.splice(bookIndex, 1);
// Return the deleted book or a success message
res.json(deletedBook[0]);
});
Step 5: Testing Your API with Postman
Your API is now complete! You can test each endpoint using Postman:
- **GET
http://localhost:3000/api/books
**: Should return the list of all 3 books. - **GET
http://localhost:3000/api/books/2
**: Should return the book with ID 2 ("1984"). - **POST
http://localhost:3000/api/books
**: Set method to POST. In the "Body" tab, select "raw" and "JSON", then add `{"title": "Dune", "author": "Frank Herbert"}`. Send it. You should get the new book back with ID 4. - **PUT
http://localhost:3000/api/books/1
**: Set method to PUT. In the body, add `{"title": "The Hobbit: An Unexpected Journey"}`. You'll get the updated book object back. - **DELETE
http://localhost:3000/api/books/3
**: Set method to DELETE. This will remove "The Great Gatsby" and return its object.
Conclusion
Congratulations! You have successfully built a fully functional REST API with Node.js and Express. You now know how to set up an Express server, define routes, handle different HTTP methods, and perform all the essential CRUD operations. This is a critical foundation for building any backend service or full-stack application. The next step would be to replace the in-memory array with a real database like PostgreSQL or MongoDB.