Topic 29 of 56 · Full Stack Advanced

Topic 4 : Using REST

Lesson TL;DRTopic 4: Using REST 📖 7 min read · 🎯 beginner · 🧭 Prerequisites: phpwithupdatedelete, runningemulatorandsimulator Why this matters Here's the thing — up until now, you might have built a frontend a...
7 min read·beginner·rest-api · express · nodejs · axios

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

MethodOperation
GETRetrieve data from the server
POSTSend data to the server to create a new resource
PUTUpdate an existing resource on the server
DELETEDelete 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.
  • :id in a route path is a route parameter accessed via req.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.

  1. Add two <input> fields — one for title, one for author — and a Submit button to Books.js.
  2. On submit, call axios.post('http://localhost:3000/books', { title, author }).
  3. On a successful response, append the returned book object to the books state 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 :id are accessed via req.params.id and 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

Like this topic? It’s one of 56 in Full Stack Advanced.

Block your seat for ₹2,500 and join the next cohort.