Building a REST API with Node.js and Express: A Step-by-Step Guide

Prashant BalePrashant Bale
6 min read

In this blog post, we'll explore how to build a REST API using Node.js and Express. We'll cover the basics of Node.js, set up our development environment, and walk through creating and managing API endpoints.

What is Node.js?

Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. It allows developers to use JavaScript to write server-side code, enabling the creation of scalable network applications.

What Can Node.js Do?

  • Server-side scripting

  • Command-line tools

  • Real-time applications (e.g., chat applications)

  • APIs for database interactions

Installation of Node.js

  1. Visit the Node.js website.

  2. Download and install the LTS (Long Term Support) version for your operating system.

  3. Verify the installation by running:

     node -v
     npm -v
    

What is NPM?

NPM (Node Package Manager) is a package manager for JavaScript. It comes bundled with Node.js and allows you to install libraries and dependencies for your project.

Install Postman

Postman is a popular tool for testing APIs.

  1. Visit the Postman website.

  2. Download and install the application for your operating system.

  3. Create a free account to save and manage your requests.

Get a Suitable IDE for Development

  • Visual Studio Code (VS Code)

  • WebStorm

  • Atom

For this tutorial, we recommend VS Code.

Start with Development - Building a Book Management API with Node.js and Express

Step 1: Create a New Project
  1. Open your terminal and create a new project directory:

     mkdir book-api
     cd book-api
    
  2. Initialize a new Node.js project:

     npm init -y
    
Step 2: Create package.json

package.json is the configuration file for your Node.js project. It contains metadata about your project and its dependencies.

Step 3: Install Express

Express is a minimal and flexible Node.js web application framework.

npm install express
Step 4: What is Express?

Express provides a robust set of features for web and mobile applications, including routing, middleware, and more.

Step 5: Install JOI

JOI is a powerful schema description language and data validator for JavaScript.

npm install joi
Step 6: What is JOI?

JOI allows you to create schemas to validate JavaScript objects in a simple and readable way.

Creating Application File

Step 7: Create app.js

Create an app.js file in your project directory:

const express = require('express');
const app = express();
const port = 3000;

app.use(express.json());

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Creating READ Request Handlers

Step 8: Define Your Data

Create a simple in-memory data store:

const books = [
  { id: 1, title: '1984', author: 'George Orwell', year: 1949 },
  { id: 2, title: 'To Kill a Mockingbird', author: 'Harper Lee', year: 1960 }
];
Step 9: Create GET Request Handlers

Add the following code to handle GET requests:

app.get('/api/books', (req, res) => {
  res.send(books);
});

app.get('/api/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send('Book not found');
  res.send(book);
});

Install Nodemon

Nodemon is a utility that monitors for changes in your source code and automatically restarts your server.

Nodemon increases productivity by automatically restarting the server whenever you make changes to your files.

npm install -g nodemon
Step 11: Handling HTTP Requests

Define the port number and create request handlers:

const port = process.env.PORT || 3000;

Creating a Function to Validate Data

Step 12: Validate Data with JOI

Define a validation schema and create a function to validate data:

const Joi = require('joi');

const validateBook = (book) => {
  const schema = Joi.object({
    title: Joi.string().min(3).required(),
    author: Joi.string().min(3).required(),
    year: Joi.number().integer().min(1000).max(new Date().getFullYear()).required()
  });
  return schema.validate(book);
};

Create Request Handlers

Step 13: Add POST Request Handler
app.post('/api/books', (req, res) => {
  const { error } = validateBook(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  const book = {
    id: books.length + 1,
    title: req.body.title,
    author: req.body.author,
    year: req.body.year
  };
  books.push(book);
  res.send(book);
});
Step 14: Add PUT Request Handler
app.put('/api/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send('Book not found');

  const { error } = validateBook(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  book.title = req.body.title;
  book.author = req.body.author;
  book.year = req.body.year;
  res.send(book);
});
Step 15: Add DELETE Request Handler
app.delete('/api/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send('Book not found');

  const index = books.indexOf(book);
  books.splice(index, 1);
  res.send(book);
});

Below is the enhanced example, which demonstrates a complete REST API for managing books, including basic CRUD operations, search functionality, and pagination. You can extend this API with additional features and data as needed for your use case.

// Import required modules
const express = require('express');
const Joi = require('joi');

// Create an instance of an Express application
const app = express();

// Define the port number
const port = 3000;

// Middleware to parse JSON bodies
app.use(express.json());

// Sample data - In-memory data store
const books = [
  { id: 1, title: '1984', author: 'George Orwell', year: 1949 },
  { id: 2, title: 'To Kill a Mockingbird', author: 'Harper Lee', year: 1960 }
];

// Function to validate book data
const validateBook = (book) => {
  const schema = Joi.object({
    title: Joi.string().min(3).required(),
    author: Joi.string().min(3).required(),
    year: Joi.number().integer().min(1000).max(new Date().getFullYear()).required()
  });
  return schema.validate(book);
};

// Route to get all books
app.get('/api/books', (req, res) => {
  res.send(books);
});

// Route to get a specific book by ID
app.get('/api/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send('Book not found');
  res.send(book);
});

// Route to create a new book
app.post('/api/books', (req, res) => {
  const { error } = validateBook(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  const book = {
    id: books.length + 1,
    title: req.body.title,
    author: req.body.author,
    year: req.body.year
  };
  books.push(book);
  res.send(book);
});

// Route to update an existing book
app.put('/api/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send('Book not found');

  const { error } = validateBook(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  book.title = req.body.title;
  book.author = req.body.author;
  book.year = req.body.year;
  res.send(book);
});

// Route to delete a book
app.delete('/api/books/:id', (req, res) => {
  const book = books.find(b => b.id === parseInt(req.params.id));
  if (!book) return res.status(404).send('Book not found');

  const index = books.indexOf(book);
  books.splice(index, 1);
  res.send(book);
});

// Route to search books by author
app.get('/api/books/search', (req, res) => {
  const { author } = req.query;
  if (!author) return res.status(400).send('Author query parameter is required');

  const filteredBooks = books.filter(b => b.author.toLowerCase().includes(author.toLowerCase()));
  res.send(filteredBooks);
});

// Route to paginate books
app.get('/api/books/paginate', (req, res) => {
  let { page = 1, size = 10 } = req.query;
  page = parseInt(page);
  size = parseInt(size);

  const startIndex = (page - 1) * size;
  const endIndex = startIndex + size;

  const paginatedBooks = books.slice(startIndex, endIndex);
  res.send(paginatedBooks);
});

// Start the server
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
Step 16: Run Your Application with Nodemon
nodemon app.js

Step 17: Test the API

You can use Postman to test the API endpoints:

Conclusion

In this tutorial, we've covered the basics of Node.js and Express, set up a development environment, and created a simple REST API with CRUD operations. By following these steps, you should now have a solid foundation for building more complex applications with Node.js and Express.

Happy Coding !!!

0
Subscribe to my newsletter

Read articles from Prashant Bale directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Prashant Bale
Prashant Bale

With 17+ years in software development and 14+ years specializing in Android app architecture and development, I am a seasoned Lead Android Developer. My comprehensive knowledge spans all phases of mobile application development, particularly within the banking domain. I excel at transforming business needs into secure, user-friendly solutions known for their scalability and durability. As a proven leader and Mobile Architect.