Topic 30 of 56 · Full Stack Advanced

Topic 5 : JSON Data

Lesson TL;DRTopic 5: JSON Data 📖 7 min read · 🎯 intermediate · 🧭 Prerequisites: buildingahttpserverwithnodejsusinghttpapis, jointquerynestedqueryfilteringdata Why this matters Up until now, you've been writing...
7 min read·intermediate·json · node-js · express · react

Topic 5: JSON Data

📖 7 min read · 🎯 intermediate · 🧭 Prerequisites: building-a-http-server-with-node-js-using-http-apis, joint-query-nested-query-filtering-data

Why this matters

Up until now, you've been writing JavaScript that mostly works with numbers and strings — values that exist in your code and stay there. But real apps need to move data around: from a file, to a server, to a browser. Here's the thing — every piece of that journey speaks the same language, and that language is JSON. Your Node server reads it, your Express API sends it, your React frontend displays it. Once you understand how JSON is structured and how to work with it in JavaScript, the whole stack starts to feel connected instead of like three separate things you're juggling at once.

What You'll Learn

  • Read and write JSON files in Node.js using the fs module
  • Build a full CRUD REST API in Express that consumes and produces JSON
  • Test Express endpoints with Postman
  • Fetch JSON data from an API inside a React component using Axios

The Analogy

Think of JSON as a universal shipping label. No matter whether a package travels from a warehouse (your database), through a sorting facility (your Express server), or lands on a customer's doorstep (your React UI), every stop along the route can read exactly the same label format — sender, contents, weight, destination. The label doesn't care what language the warehouse clerk speaks or what OS the sorting machine runs; the structure is the contract. JSON works the same way: a lightweight, human-readable text format that every modern language can parse and generate, making it the universal envelope for data on the web.

Chapter 1: Introduction to JSON

JSON — JavaScript Object Notation — is a text format for representing structured data based on JavaScript object syntax. Despite its name it is language-agnostic; virtually every programming environment has a parser for it. It is the default data interchange format for web APIs, configuration files, and inter-service communication.

JSON supports six value types:

  • String"hello"
  • Number42 or 3.14
  • Booleantrue / false
  • Nullnull
  • Array — ordered list: [1, 2, 3]
  • Object — key/value map: {"key": "value"}

A typical JSON document looks like this:

{
    "name": "Alice",
    "age": 30,
    "email": "alice@example.com",
    "skills": ["JavaScript", "Node.js", "React"]
}

Every key must be a double-quoted string. Values follow the type rules above. Trailing commas are not allowed — a common gotcha.

Chapter 2: Reading and Writing JSON Files

Node.js ships with the fs (file system) module, which lets you read and write files on disk. Combined with the built-in JSON.parse() and JSON.stringify() methods, you have everything you need to persist JSON data locally.

Step 1: Reading a JSON File

readJsonFile.js:

const fs = require('fs');

fs.readFile('data.json', 'utf8', (err, data) => {
    if (err) {
        console.error('Error reading file:', err);
        return;
    }
    const jsonData = JSON.parse(data);
    console.log('JSON data:', jsonData);
});

fs.readFile is asynchronous — it hands you the raw file string via callback. JSON.parse() converts that string into a live JavaScript object you can work with.

Run it:

node readJsonFile.js

Step 2: Writing to a JSON File

writeJsonFile.js:

const fs = require('fs');

const jsonData = {
    name: 'Bob',
    age: 25,
    email: 'bob@example.com',
    skills: ['Python', 'Django', 'Flask']
};

fs.writeFile('output.json', JSON.stringify(jsonData, null, 2), 'utf8', (err) => {
    if (err) {
        console.error('Error writing file:', err);
        return;
    }
    console.log('File has been saved.');
});

JSON.stringify(jsonData, null, 2) serializes the object back into a string. The third argument 2 is the indent size — omit it and you get a single-line blob; include it and you get a readable, pretty-printed file.

Run it:

node writeJsonFile.js

After running, inspect output.json — you should see a neatly indented file with Bob's details.

Chapter 3: Using JSON with Express

Express makes serving and consuming JSON trivial. The key is the express.json() middleware, which automatically parses incoming Content-Type: application/json request bodies and attaches the result to req.body.

Step 1: Set Up an Express Application

server.js:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

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

// Sample data
let users = [
    { id: 1, name: 'Alice', age: 30, email: 'alice@example.com' },
    { id: 2, name: 'Bob', age: 25, email: 'bob@example.com' }
];

// GET endpoint to return all users
app.get('/users', (req, res) => {
    res.json(users);
});

// POST endpoint to add a new user
app.post('/users', (req, res) => {
    const newUser = req.body;
    newUser.id = users.length + 1;
    users.push(newUser);
    res.status(201).json(newUser);
});

// PUT endpoint to update a user
app.put('/users/:id', (req, res) => {
    const userId = parseInt(req.params.id);
    const updatedUser = req.body;
    const userIndex = users.findIndex(user => user.id === userId);
    if (userIndex === -1) {
        return res.status(404).json({ error: 'User not found' });
    }
    users[userIndex] = { id: userId, ...updatedUser };
    res.json(users[userIndex]);
});

// DELETE endpoint to delete a user
app.delete('/users/:id', (req, res) => {
    const userId = parseInt(req.params.id);
    users = users.filter(user => user.id !== userId);
    res.status(204).send();
});

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

Run the server:

node server.js

The four routes form a complete CRUD surface:

MethodRouteAction
GET/usersReturn all users
POST/usersAdd a new user
PUT/users/:idReplace a user by ID
DELETE/users/:idRemove a user by ID

Step 2: Testing the Endpoints with Postman

Open Postman and fire each request in turn.

1. GET /users

  • URL: http://localhost:3000/users
  • Method: GET
  • Send the request to retrieve all users.

2. POST /users

  • URL: http://localhost:3000/users
  • Method: POST
  • Body (JSON):
{
    "name": "Charlie",
    "age": 28,
    "email": "charlie@example.com"
}
  • Send the request to add a new user.

3. PUT /users/:id

  • URL: http://localhost:3000/users/1
  • Method: PUT
  • Body (JSON):
{
    "name": "Alice Updated",
    "age": 31,
    "email": "alice.updated@example.com"
}
  • Send the request to update the user with ID 1.

4. DELETE /users/:id

  • URL: http://localhost:3000/users/2
  • Method: DELETE
  • Send the request to delete the user with ID 2.
sequenceDiagram
    participant Client as Postman / React
    participant Express as Express Server
    participant Store as In-Memory Users Array

    Client->>Express: GET /users
    Express->>Store: read users[]
    Store-->>Express: [Alice, Bob]
    Express-->>Client: 200 JSON array

    Client->>Express: POST /users {Charlie}
    Express->>Store: push new user
    Store-->>Express: updated array
    Express-->>Client: 201 JSON {id:3, ...}

    Client->>Express: PUT /users/1 {Alice Updated}
    Express->>Store: findIndex + replace
    Store-->>Express: updated user
    Express-->>Client: 200 JSON updated user

    Client->>Express: DELETE /users/2
    Express->>Store: filter out id=2
    Express-->>Client: 204 No Content

Chapter 4: Fetching JSON Data in a React Application

With the Express server running, the next step is consuming that JSON from a React frontend. The class used Axios — a promise-based HTTP client that handles JSON serialization and deserialization automatically.

Step 1: Create a New React Project

npx create-react-app json-data-demo
cd json-data-demo
npm install axios

Step 2: Fetch Data in a Component

src/components/Users.js:

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function Users() {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        axios.get('http://localhost:3000/users')
            .then((response) => {
                setUsers(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>Users</h1>
            <ul>
                {users.map((user) => (
                    <li key={user.id}>
                        {user.name} - {user.email}
                    </li>
                ))}
            </ul>
        </div>
    );
}

export default Users;

The empty dependency array [] on useEffect means the fetch runs once on mount. response.data is already a parsed JavaScript object — Axios handles JSON.parse for you.

Step 3: Integrate the Component into the App

src/App.js:

import React from 'react';
import './App.css';
import Users from './components/Users';

function App() {
    return (
        <div className="App">
            <header className="App-header">
                <h1>JSON Data Demo</h1>
            </header>
            <Users />
        </div>
    );
}

export default App;

Step 4: Run the React Application

npm start

Navigating to http://localhost:3000 in a web browser displays the list of users fetched from the Express API. Make sure the Express server (node server.js) is still running in a separate terminal when you start the React app.

🧪 Try It Yourself

Task: Add a POST form to the React app that lets you create a new user and immediately see them appear in the list.

  1. Add a form with three inputs — name, age, email — and a Submit button to Users.js.
  2. On submit, call axios.post('http://localhost:3000/users', formData).
  3. On a successful response, append response.data to the users state array.

Success criterion: After you fill in the form and click Submit, the new user's name and email appear at the bottom of the list — no page refresh needed.

Starter snippet (add inside the Users component):

const [form, setForm] = useState({ name: '', age: '', email: '' });

const handleSubmit = (e) => {
    e.preventDefault();
    axios.post('http://localhost:3000/users', form)
        .then((response) => {
            setUsers((prev) => [...prev, response.data]);
            setForm({ name: '', age: '', email: '' });
        });
};

// In your JSX:
// <form onSubmit={handleSubmit}>
//   <input value={form.name} onChange={e => setForm({...form, name: e.target.value})} placeholder="Name" />
//   <input value={form.age}  onChange={e => setForm({...form, age: e.target.value})}  placeholder="Age" />
//   <input value={form.email} onChange={e => setForm({...form, email: e.target.value})} placeholder="Email" />
//   <button type="submit">Add User</button>
// </form>

🔍 Checkpoint Quiz

Q1. What does the third argument 2 do in JSON.stringify(data, null, 2)?

A) It limits the output to 2 levels of nesting
B) It sets the indentation to 2 spaces, producing pretty-printed output
C) It tells stringify to skip the first 2 keys
D) It converts numbers to 2 decimal places

Q2. Given this Express route, what HTTP status code is returned when a user with the requested ID does not exist?

app.put('/users/:id', (req, res) => {
    const userIndex = users.findIndex(user => user.id === parseInt(req.params.id));
    if (userIndex === -1) {
        return res.status(404).json({ error: 'User not found' });
    }
    users[userIndex] = { id: parseInt(req.params.id), ...req.body };
    res.json(users[userIndex]);
});

A) 200
B) 201
C) 404
D) 500

Q3. In the React Users component, why is the dependency array of useEffect left empty ([])?

A) It causes the effect to re-run every time any state changes
B) It prevents the component from rendering
C) It makes the fetch run exactly once when the component first mounts
D) It disables the loading state

Q4. You have a Node.js app that reads config.json with fs.readFile. A teammate reports that the parsed object always shows outdated values even after they edited the file. What is the most likely cause, and how would you confirm it?

A1. B — The third argument to JSON.stringify is the space parameter. Passing 2 inserts two-space indentation at each nesting level, making the output human-readable.

A2. C — The route explicitly calls res.status(404).json({ error: 'User not found' }) when findIndex returns -1, which means no match was found.

A3. C — An empty dependency array tells React "this effect has no dependencies that change," so it fires once after the initial render (mount) and never again. This is the standard pattern for a one-time data fetch on component load.

A4. The most likely cause is that require('fs') is not the issue — but if they switched to require('./config.json'), Node.js caches that at startup and never re-reads the file. Confirm by logging data immediately after fs.readFile — if readFile is used correctly, the latest file contents will appear. If require is being used instead, replace it with fs.readFile + JSON.parse to bypass the module cache.

🪞 Recap

  • JSON is a text-based, language-agnostic data format built on JavaScript object syntax, supporting strings, numbers, booleans, null, arrays, and objects.
  • fs.readFile + JSON.parse reads a JSON file into a live JS object; JSON.stringify + fs.writeFile persists it back to disk.
  • The express.json() middleware must be registered before any route that reads req.body from a JSON request.
  • A four-route Express API (GET, POST, PUT, DELETE) gives you a complete CRUD surface over JSON data.
  • Axios in React automatically deserializes JSON responses into response.data, pairing cleanly with useState and useEffect for async data fetching.

📚 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.