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.