Topic 4: Using REST
📖 7 min read · 🎯 beginner · 🧭 Prerequisites: php-with-update-delete, running-emulator-and-simulator
Why this matters
Here's the thing — up until now, you might have built a frontend and a backend separately, but they couldn't really talk to each other in a clean, reliable way. REST changes that. REST is a set of simple rules that turns HTTP — the same protocol your browser uses every day — into a predictable contract between your frontend, your backend, and any external service. Once you know REST, any system can communicate with any other system, in any language. Today we're going to build a live RESTful API with Express, test every endpoint in Postman, and connect a React client to it using Axios.
What You'll Learn
- Understand the four core HTTP methods (GET, POST, PUT, DELETE) and what operation each performs
- Build a fully functional RESTful API in Node.js using the Express framework
- Test API endpoints interactively with Postman and cURL
- Consume a RESTful API from a React application using the Axios HTTP client
The Analogy
Think of a RESTful API as a reference desk at a city library. When you walk up and say "I'd like the book with ID 42," the librarian hands it to you — that's a GET. When you donate a new book, they log it in the catalog — that's a POST. When you return a book with corrections to its record, they update the entry — that's a PUT. And when a book is decommissioned, they pull it from the shelf entirely — that's a DELETE. The desk doesn't remember you between visits (stateless), every request carries everything the librarian needs, and the same rules apply whether you're a student, a researcher, or a robot arm sorting shelves.
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. Every resource in a REST API has a URL, and you interact with that resource using standard HTTP methods.
Key HTTP Methods
| Method | Operation |
|---|---|
| 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 |
REST is stateless: the server holds no session memory between requests. Each request must contain all the information needed to fulfill it. This makes REST APIs scalable, cacheable, and easy to reason about.
sequenceDiagram
participant Client
participant Server
Client->>Server: GET /books
Server-->>Client: 200 OK [ {id:1,...}, {id:2,...} ]
Client->>Server: POST /books { title, author }
Server-->>Client: 201 Created { id:3, title, author }
Client->>Server: PUT /books/1 { title, author }
Server-->>Client: 200 OK { id:1, title, author }
Client->>Server: DELETE /books/1
Server-->>Client: 200 OK { id:1, ... }
Chapter 2: Setting Up a RESTful API with Express
The class chose Express — the minimal, fast Node.js web framework — to build a book-management API.
Step 1: Set Up the Project
Create a new directory, initialize the project, and 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 in the project root. This single file defines the Express app, seeds in-memory data, and registers all five route handlers.
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}`);
});
Key details worth noting:
app.use(express.json())parses incoming JSON request bodies — required for POST and PUT.:idin a route path is a route parameter accessed viareq.params.id.parseInt()is necessary because route parameters are strings by default.res.status(201)signals a successful resource creation (distinct from a generic 200 OK).res.status(404).send(...)returns a plain-text error when a book is not found.
Step 3: Run the Server
node server.js
The API is now live at http://localhost:3000. You can interact with it using Postman or cURL.
Chapter 3: Testing the RESTful API with Postman
Postman is a GUI tool that lets you fire HTTP requests without writing any client code. It's the fastest way to verify your API before wiring up a frontend.
Step 1: Test GET Requests
GET All Books
- URL:
http://localhost:3000/books - Method:
GET - Send the request to retrieve all books. You should see the full array returned as JSON.
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 → JSON and send:
{
"title": "To Kill a Mockingbird",
"author": "Harper Lee"
}
The server responds with HTTP 201 and the newly created book object, including its assigned id.
Step 3: Test PUT Request
PUT to Update a Book
- URL:
http://localhost:3000/books/1 - Method:
PUT - Body (raw JSON):
{
"title": "Animal Farm",
"author": "George Orwell"
}
The server returns the updated book object with the new title and author.
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 responds with the deleted book object.
Chapter 4: Using Axios to Consume the RESTful API in React
With the API proven in Postman, the class built a React client. They chose Axios — a promise-based HTTP client — over the native fetch API for its cleaner error handling and automatic JSON parsing.
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 src/components/Books.js. This component fetches the full book list from the Express API on mount and renders it.
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;
The empty dependency array [] in useEffect ensures the fetch runs exactly once — when the component mounts. The three-state pattern (loading, error, data) gracefully handles all possible outcomes of the async call.
Step 3: Integrate the Component into the App
Update src/App.js to render the Books component inside the main layout.
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
npm start
Navigate to http://localhost:3000 in a web browser. The page displays the list of books fetched live from the Express API. Make sure the Express server is still running on port 3000 (or adjust the Axios URL if you used a different port).
🧪 Try It Yourself
Task: Extend the Books React component to support adding a new book via a form.
- Add two
<input>fields — one fortitle, one forauthor— and a Submit button toBooks.js. - On submit, call
axios.post('http://localhost:3000/books', { title, author }). - On a successful response, append the returned book object to the
booksstate array.
Success criterion: After submitting the form, the new book title and author appear instantly in the list below — without refreshing the page.
Starter snippet for the form and submit handler:
const [title, setTitle] = useState('');
const [author, setAuthor] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
axios.post('http://localhost:3000/books', { title, author })
.then((response) => {
setBooks([...books, response.data]);
setTitle('');
setAuthor('');
});
};
// JSX to add inside your return:
// <form onSubmit={handleSubmit}>
// <input value={title} onChange={e => setTitle(e.target.value)} placeholder="Title" />
// <input value={author} onChange={e => setAuthor(e.target.value)} placeholder="Author" />
// <button type="submit">Add Book</button>
// </form>
🔍 Checkpoint Quiz
Q1. REST is described as "stateless." What does that mean for a RESTful API server?
A) The server stores user sessions in memory between requests
B) The server holds no session memory — each request must contain all the information needed to fulfill it
C) The server only accepts requests that include a cookie
D) The server caches all responses indefinitely
Q2. Given this route handler:
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);
});
A request arrives for GET /books/abc. What happens?
A) The server crashes with an unhandled exception
B) parseInt('abc') returns NaN; b.id === NaN is always false; the server returns 404 "Book not found"
C) Express rejects the request before it reaches the handler
D) The server returns the first book in the array as a fallback
Q3. In the Books React component, why is the useEffect dependency array left empty ([])?
A) It is a mistake — it should contain books so the fetch re-runs whenever the list changes
B) An empty array means the effect runs after every render
C) An empty array means the effect runs only once, on mount — preventing an infinite fetch loop
D) Axios requires an empty dependency array to function correctly
Q4. You need to let users delete a book from the React client. Which Axios call and which Express route would pair correctly?
A) axios.remove(...) on the client; app.get('/books/:id', ...) on the server
B) axios.delete('http://localhost:3000/books/2') on the client; app.delete('/books/:id', ...) on the server
C) axios.post('http://localhost:3000/books/delete/2') on the client; app.post('/books/delete/:id', ...) on the server
D) axios.put(...) on the client; app.delete(...) on the server
A1. B — Statelessness means the server retains no memory of prior requests. Every HTTP call is self-contained, which is what makes REST APIs independently scalable.
A2. B — parseInt('abc') produces NaN. The strict equality check b.id === NaN is false for every book (NaN is not equal to anything, including itself), so find returns undefined and the handler responds with 404.
A3. C — The empty array [] tells React to run the effect exactly once after the initial render. Omitting it would re-run the fetch after every state update, including the one that sets books, causing an infinite loop.
A4. B — Axios mirrors the HTTP verb in its method name (axios.delete), and the corresponding Express handler is registered with app.delete. REST convention maps the DELETE HTTP method to resource removal.
🪞 Recap
- REST is a stateless, client-server architectural style that uses HTTP methods (GET, POST, PUT, DELETE) to operate on named resources.
- Express makes it straightforward to register route handlers for each HTTP method and parse JSON request bodies with
app.use(express.json()). - Route parameters like
:idare accessed viareq.params.idand must be cast (e.g.,parseInt) to match numeric IDs. - Postman lets you test all four HTTP methods interactively before writing any frontend code.
- Axios in React provides a clean promise-based interface for consuming REST APIs, and the
useEffect+ empty dependency array pattern is the standard way to fetch data on component mount.
📚 Further Reading
- Express.js Routing Docs — the source of truth on defining routes, parameters, and middleware in Express
- Axios GitHub Repository — full API reference, interceptors, and advanced configuration for the Axios HTTP client
- MDN HTTP Methods Reference — authoritative documentation on GET, POST, PUT, DELETE, and the rest of the HTTP verb family
- REST API Design Best Practices — practical guidance on naming conventions, status codes, and versioning for production APIs
- ⬅️ Previous: Running Emulator and Simulator
- ➡️ Next: Working with Asynchronous Programming