Topic 4: Using REST
📖 7 min read · 🎯 intermediate · 🧭 Prerequisites: postman-configuration, working-with-package-lock-json-to-lock-the-node-modules-versions
Why this matters
Up until now, you've been writing backend code that just sits there — it can't talk to anything outside itself. But real applications need to communicate: a React front-end needs data from the server, a mobile app needs to send user info, different services need to exchange information. That's exactly what REST APIs are for. In this lesson, we build a proper RESTful API from scratch using Node.js and Express, test it with Postman, and then connect a React client to it using Axios — so your backend finally has something to say to the world.
What You'll Learn
- Understand the four core HTTP methods — GET, POST, PUT, DELETE — and what each one does
- Build a fully functional RESTful API with Express, covering all CRUD operations
- Test every route using Postman with real request bodies and status codes
- Consume a REST API from a React application using Axios
The Analogy
Think of a REST API as a well-run post office in Vizag. Every package (resource) has a unique address (URL). When you want to see what's in a package, you send a GET slip. When you want to drop off a new package, you hand over a POST form. To replace what's inside an existing package, you file a PUT amendment. And when a package is no longer needed, you submit a DELETE request. The post office clerks (the server) never remember your previous visit — each transaction is completely self-contained — which is exactly what "stateless" means in REST.
Chapter 1: Introduction to REST
REST (Representational State Transfer) is an architectural style for designing networked applications. It relies on a stateless, client-server communication protocol — almost always HTTP — where each request from the client contains all the information the server needs to fulfill it.
Key HTTP Methods
- GET — Retrieve data from the server.
- POST — Send data to the server to create a new resource.
- PUT — Update an existing resource on the server.
- DELETE — Delete a resource from the server.
Each method maps cleanly to a CRUD operation (Create, Read, Update, Delete), and together they cover the full lifecycle of any resource your API exposes.
graph LR
Client -->|GET /books| Server
Client -->|POST /books| Server
Client -->|PUT /books/:id| Server
Client -->|DELETE /books/:id| Server
Server -->|JSON response| Client
Chapter 2: Setting Up a RESTful API with Express
The class decided to build a simple RESTful API to manage a list of books — a fitting choice for a city that runs on knowledge.
Step 1: Set Up the Project
Create a new directory for the project and initialise a Node.js project, then install Express.
mkdir rest-api-demo
cd rest-api-demo
npm init -y
npm install express
Step 2: Create the Server File
Create server.js to handle all incoming HTTP requests. The in-memory books array serves as the data store for this demo.
server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
let books = [
{ id: 1, title: '1984', author: 'George Orwell' },
{ id: 2, title: 'Brave New World', author: 'Aldous Huxley' },
];
// GET all books
app.get('/books', (req, res) => {
res.json(books);
});
// GET a book by ID
app.get('/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.json(book);
});
// POST a new book
app.post('/books', (req, res) => {
const newBook = {
id: books.length + 1,
title: req.body.title,
author: req.body.author
};
books.push(newBook);
res.status(201).json(newBook);
});
// PUT to update a book
app.put('/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');
book.title = req.body.title;
book.author = req.body.author;
res.json(book);
});
// DELETE a book
app.delete('/books/:id', (req, res) => {
const bookIndex = books.findIndex(b => b.id === parseInt(req.params.id));
if (bookIndex === -1) return res.status(404).send('Book not found');
const deletedBook = books.splice(bookIndex, 1);
res.json(deletedBook);
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Step 3: Run the Server
node server.js
The API is now running at http://localhost:3000. You can interact with it using Postman or cURL.
Chapter 3: Testing the RESTful API with Postman
the trainer walked the class through each HTTP method in Postman, one at a time.
Step 1: Test GET Requests
GET All Books
- URL:
http://localhost:3000/books - Method:
GET - Send the request to retrieve all books. The response will be a JSON array containing both entries.
GET Book by ID
- URL:
http://localhost:3000/books/1 - Method:
GET - Send the request to retrieve the book with ID 1.
Step 2: Test POST Request
POST a New Book
- URL:
http://localhost:3000/books - Method:
POST - Set the body type to
raw/JSONand send:
{
"title": "To Kill a Mockingbird",
"author": "Harper Lee"
}
The server responds with 201 Created and the newly created book object, including its auto-assigned id.
Step 3: Test PUT Request
PUT to Update a Book
- URL:
http://localhost:3000/books/1 - Method:
PUT - Body:
{
"title": "Animal Farm",
"author": "George Orwell"
}
The server responds with the updated book object. The record at ID 1 is now "Animal Farm".
Step 4: Test DELETE Request
DELETE a Book
- URL:
http://localhost:3000/books/1 - Method:
DELETE - Send the request to delete the book with ID 1. The server returns the deleted book object as confirmation.
Chapter 4: Using Axios to Consume the RESTful API in React
With the API proven in Postman, the class turned to building a React client that talks to it automatically on page load, using the Axios HTTP library.
Step 1: Create a New React Project
npx create-react-app book-api-client
cd book-api-client
npm install axios
Step 2: Create a Component to Fetch and Display Data
Create a Books component that fetches the list from the API inside a useEffect hook and renders it.
src/components/Books.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function Books() {
const [books, setBooks] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios.get('http://localhost:3000/books')
.then((response) => {
setBooks(response.data);
setLoading(false);
})
.catch((error) => {
setError(error);
setLoading(false);
});
}, []);
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return (
<div>
<h1>Books</h1>
<ul>
{books.map((book) => (
<li key={book.id}>
{book.title} - {book.author}
</li>
))}
</ul>
</div>
);
}
export default Books;
Step 3: Integrate the Component into the App
Wire the Books component into the root App component.
src/App.js
import React from 'react';
import './App.css';
import Books from './components/Books';
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Book API Client</h1>
</header>
<Books />
</div>
);
}
export default App;
Step 4: Run the React Application
Make sure your Express server is still running on port 3000, then start the React dev server (it will pick a different port automatically):
npm start
Navigating to http://localhost:3001 (or whichever port CRA assigns) in a browser will display the list of books fetched live from the Express API.
🧪 Try It Yourself
Task: Add a PATCH /books/:id route to server.js that allows partial updates — meaning the caller can update only title, only author, or both, without overwriting the field they did not send.
Success criterion: Sending the following body to PATCH http://localhost:3000/books/2 should update only the title and leave the author unchanged:
{
"title": "Island"
}
Starter snippet — drop this into server.js after the PUT route:
// PATCH to partially update a book
app.patch('/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');
// your logic here
res.json(book);
});
🔍 Checkpoint Quiz
Q1. REST is described as "stateless." What does that mean in practice?
A) The server stores session data in memory between requests
B) Each request must contain all the information the server needs; no session state is kept server-side
C) The client cannot send any state in the request body
D) REST only works without cookies
Q2. Given this Express route handler:
app.post('/books', (req, res) => {
const newBook = {
id: books.length + 1,
title: req.body.title,
author: req.body.author
};
books.push(newBook);
res.status(201).json(newBook);
});
What HTTP status code does a successful POST return, and why is 201 preferred over 200 here?
Q3. In the React Books component, why is axios.get(...) called inside a useEffect hook with an empty dependency array []?
A) To call the API every time a book is added
B) To prevent the component from rendering at all until data arrives
C) To run the fetch exactly once after the component first mounts
D) Because Axios requires useEffect to function correctly
Q4. You want to permanently remove the book with ID 3. Which Postman configuration is correct?
A) Method: PUT, URL: http://localhost:3000/books/3, body: { "delete": true }
B) Method: DELETE, URL: http://localhost:3000/books/3, no body required
C) Method: GET, URL: http://localhost:3000/books/delete/3
D) Method: POST, URL: http://localhost:3000/books/3, body: { "action": "delete" }
A1. B — Stateless means the server holds no memory of prior requests; every call is self-contained. This makes REST APIs easier to scale horizontally.
A2. 201 Created. HTTP 200 signals a generic success, but 201 specifically communicates that a new resource was created as a result of the request — more semantically precise and machine-readable by API consumers.
A3. C — An empty dependency array tells React to run the effect only once, right after the initial render (equivalent to componentDidMount). Without it, the effect would re-run on every render, causing an infinite fetch loop.
A4. B — The DELETE method on the resource URL is the correct RESTful approach. No request body is needed; the resource is identified entirely by its URL parameter.
🪞 Recap
- REST is a stateless, HTTP-based architectural style where GET, POST, PUT, and DELETE map directly to read, create, update, and delete operations.
- Express makes it straightforward to define routes for each HTTP method using
app.get(),app.post(),app.put(), andapp.delete(). app.use(express.json())must be registered as middleware before any route that readsreq.body, or the body will beundefined.- Postman lets you manually fire any HTTP method with a custom body and inspect the exact status code and JSON response your API returns.
- Axios in a React component, called inside
useEffectwith[], is the standard pattern for fetching data from a REST API on component mount.
📚 Further Reading
- Express.js Routing docs — the source of truth for defining routes, parameters, and middleware chains
- MDN HTTP Methods reference — authoritative breakdown of every HTTP verb and its semantics
- Axios documentation — full API reference for making HTTP requests from Node.js and the browser
- REST API Design Best Practices — practical guidance on naming conventions, versioning, and status code usage
- ⬅️ Previous: Working with package-lock.json to Lock the Node Modules Versions
- ➡️ Next: Working with Asynchronous Programming