Intermediate35 minjavascriptjsonarraysdata

Working with JSON

Understand what JSON is, how to parse and stringify it, and how to use array methods to reshape and filter real API data into exactly what your UI needs.

Learning Objectives

By the end of this lesson, you'll be able to:

  • βœ“ Explain Explain what JSON is and how it differs from a JavaScript object
  • βœ“ Use `JSON.parse()` and `JSON.stringify()` correctly
  • βœ“ Navigate and extract values from nested JSON structures
  • βœ“ Use `map()`, `filter()`, and `find()` to reshape and query API data
  • βœ“ Recognise and avoid the most common JSON gotchas

Why This Matters:

Understand what JSON is, how to parse and stringify it, and how to use array methods to reshape and filter real API data into exactly what your UI needs.

Before You Start:

You should be familiar with:

What is JSON?

JSON stands for JavaScript Object Notation. It's a text format for representing structured data β€” and it's the universal language of web APIs. When a server sends you data, it's almost always JSON.

JSON looks very much like a JavaScript object literal:

{
  "id": 1,
  "name": "Helen Burgess",
  "email": "helen@example.com",
  "active": true,
  "score": null
}

But it's not an object. It's a string. A very structured string β€” but a string. That's why you need to parse it before you can work with it.


JSON pipeline from raw text to parsed JavaScript object to UI-ready shape.Raw JSON text`\"name\": \"Helen\"`still just charactersParsed object`data.user.name`usable in JavaScriptUI-ready shape`{ title, city, company }`clean and focusedparsereshape
JSON starts as text, becomes JavaScript data in memory, then gets reshaped into something your interface can actually render.

JSON syntax rules

JSON is stricter than JavaScript object notation. A few key differences:

RuleJSONJavaScript object
KeysMust be double-quoted stringsCan be unquoted identifiers
StringsDouble quotes onlySingle or double quotes
Trailing commasNot allowedAllowed in modern JS
FunctionsNot allowedAllowed
undefinedNot allowedAllowed
CommentsNot allowedAllowed

Valid JSON value types are: string, number, boolean, null, array, and object. That's it.

{
  "title": "Espresso Machine",
  "price": 299,
  "inStock": true,
  "discount": null,
  "tags": ["coffee", "appliance"],
  "dimensions": {
    "width": 28,
    "height": 40
  }
}

JSON.parse() β€” string to object

When you receive JSON from an API, you convert it to a JavaScript object using JSON.parse():

const jsonString = '{"name":"Helen","score":42}';

const user = JSON.parse(jsonString);
console.log(user.name);  // 'Helen'
console.log(user.score); // 42

When you use fetch() and call .json() on the response, this parsing happens for you automatically. But JSON.parse() is useful when you're reading JSON from local storage, a file, or any source that gives you a raw string.

If the string isn't valid JSON, JSON.parse() throws a SyntaxError. It's worth wrapping it in a try/catch when the source isn't guaranteed to be valid:

try {
  const data = JSON.parse(rawString);
  console.log(data);
} catch (error) {
  console.error('Invalid JSON:', error.message);
}

JSON.stringify() β€” object to string

To go the other direction β€” converting a JavaScript object into a JSON string β€” use JSON.stringify():

const user = { name: 'Helen', role: 'admin' };
const jsonString = JSON.stringify(user);

console.log(jsonString);         // '{"name":"Helen","role":"admin"}'
console.log(typeof jsonString);  // 'string'

You saw this in the previous tutorial when sending a POST request:

body: JSON.stringify({ title: 'My post', userId: 1 })

JSON.stringify() also accepts optional arguments for formatting. A third argument of 2 adds readable indentation:

console.log(JSON.stringify(user, null, 2));
// {
//   "name": "Helen",
//   "role": "admin"
// }

Useful for debugging β€” especially when you're logging complex nested objects.


API responses are often deeply nested. Here's a realistic example:

{
  "user": {
    "id": 1,
    "name": "Helen",
    "address": {
      "city": "Perth",
      "postcode": "6020"
    },
    "orders": [
      { "id": 101, "total": 89.50, "status": "delivered" },
      { "id": 102, "total": 145.00, "status": "pending" }
    ]
  }
}

Once parsed, you access nested values with dot notation and bracket notation exactly as you would any JavaScript object:

const data = JSON.parse(jsonString);

console.log(data.user.name);              // 'Helen'
console.log(data.user.address.city);     // 'Perth'
console.log(data.user.orders[0].total);  // 89.50
console.log(data.user.orders[1].status); // 'pending'

When a path might not exist, optional chaining (?.) protects you from errors:

console.log(data.user?.phone?.mobile); // undefined β€” no error thrown

Reshaping data with array methods

API responses rarely arrive in the exact shape your UI needs. You'll almost always need to extract, filter, or transform the data before using it. This is where array methods earn their keep.

map() β€” transform each item

Use map() to convert an array of objects into a new array of a different shape.

const posts = [
  { id: 1, title: 'First post', body: '...', userId: 1 },
  { id: 2, title: 'Second post', body: '...', userId: 1 },
];

// Extract just the titles
const titles = posts.map((post) => post.title);
console.log(titles); // ['First post', 'Second post']

// Create a simpler object shape for the UI
const summaries = posts.map((post) => ({
  id: post.id,
  heading: post.title,
}));

filter() β€” keep matching items

Use filter() to return only the items that meet a condition.

const orders = [
  { id: 101, status: 'delivered', total: 89.50 },
  { id: 102, status: 'pending',   total: 145.00 },
  { id: 103, status: 'delivered', total: 32.00 },
];

const delivered = orders.filter((order) => order.status === 'delivered');
// [{ id: 101, ... }, { id: 103, ... }]

const over100 = orders.filter((order) => order.total > 100);
// [{ id: 102, ... }]

find() β€” get one matching item

Use find() when you need a single item β€” by ID, for example.

const users = [
  { id: 1, name: 'Helen' },
  { id: 2, name: 'Gordon' },
  { id: 3, name: 'Sue' },
];

const user = users.find((u) => u.id === 2);
console.log(user.name); // 'Gordon'

find() returns the first match, or undefined if nothing matches. Always check before using the result:

const user = users.find((u) => u.id === 99);
if (user) {
  console.log(user.name);
} else {
  console.log('User not found');
}

Combining methods

Methods chain together β€” and this is where they become powerful:

fetch('https://jsonplaceholder.typicode.com/posts')
  .then((response) => response.json())
  .then((posts) => {
    // Get titles of the first 3 posts by user 1
    const result = posts
      .filter((post) => post.userId === 1)
      .slice(0, 3)
      .map((post) => post.title);

    console.log(result);
  });

API sends

  • all fields
  • extra nesting
  • more records than you need

UI keeps

  • only relevant items
  • only relevant fields
  • clear display order
Array methods help you move from β€œeverything the API sent” to β€œonly what this UI needs right now.”

Common JSON gotchas

1. undefined becomes nothing

JSON.stringify() silently drops properties with undefined values:

const obj = { name: 'Helen', phone: undefined };
JSON.stringify(obj); // '{"name":"Helen"}' β€” phone is gone

Use null when you want to represent "no value" in JSON.

2. Dates are strings

JSON has no Date type. Dates are serialised as ISO strings:

JSON.stringify({ created: new Date() });
// '{"created":"2026-04-28T01:00:00.000Z"}'

When you parse them back, they're still strings β€” not Date objects. Convert them explicitly if you need date arithmetic:

const created = new Date(data.created);

3. Numbers can lose precision

JSON handles standard numbers fine, but very large integers (e.g., database IDs from some systems) can lose precision when parsed. This is rarely a problem with typical web APIs, but worth knowing.


⏸️ Check Your Understanding

Before moving forward, can you answer these?

  1. 1. What's the key difference between a JSON string and a JavaScript object?
  2. 2. Why does `JSON.stringify()` drop `undefined` values?
  3. 3. When would you use `filter()` vs `find()`?
  4. 4. Why should you use `null` rather than `undefined` in data you're going to serialise?
Check Your Answers
  1. JSON is text β€” a string. A JavaScript object is a data structure in memory. You convert between them with `JSON.parse()` (string β†’ object) and `JSON.stringify()` (object β†’ string).
  2. Because `undefined` is not a valid JSON value type. JSON only supports strings, numbers, booleans, null, arrays, and objects.
  3. Use `filter()` when you want all items that match a condition (returns an array). Use `find()` when you want the first single item that matches (returns one item or `undefined`).
  4. `undefined` is dropped by `JSON.stringify()`, so it can't be represented in JSON. `null` is a valid JSON value and survives the conversion intact.

How confident are you with this concept?

πŸ˜• Still confused | πŸ€” Getting there | 😊 Got it! | πŸŽ‰ Could explain it to a friend!

Guided Practice

<!-- GuidedPractice component -->

Step 1 β€” Fetch the todos

Fetch from https://jsonplaceholder.typicode.com/todos with the full response.ok check pattern.

Step 2 β€” Filter to completed items only

Use filter() on the returned array to keep only todos where completed === true.

Step 3 β€” Extract the titles

Use map() on the filtered array to produce an array of title strings.

Step 4 β€” Limit to the first 5

Use .slice(0, 5) to keep only the first five results.

Step 5 β€” Log the result

Log the final array. You should see five completed todo titles.

Hint β€” chaining the methods

const titles = todos
  .filter((todo) => todo.completed === true)
  .slice(0, 5)
  .map((todo) => todo.title);

Step 6 β€” Stringify for inspection

Log JSON.stringify(titles, null, 2) and compare it to the plain console.log output. Notice the difference in how it displays.

πŸ’ͺ Independent Practice

<!-- IndependentPractice component -->

Your Task:

Build a user directory from API data.

Requirements:
  • Fetch users from `https://jsonplaceholder.typicode.com/users`
  • Use `map()` to reshape each user into this simpler object:
  • Use `filter()` to keep only users whose company name includes the word `'LLC'`
  • Log the filtered, reshaped array using `JSON.stringify()` with indentation
  • Add a `find()` call that looks up a specific user by `id` from the *original* unfiltered array and logs their full name and email

Success Criteria:

CriteriaYou've succeeded if...
The fetch uses the full `response.ok` patternCompleted clearly and correctly in your solution.
Each item in the reshaped array has exactly the four properties listed aboveCompleted clearly and correctly in your solution.
The filter correctly matches company names containing `'LLC'`Completed clearly and correctly in your solution.
The `find()` lookup targets a specific ID and guards against `undefined`Completed clearly and correctly in your solution.
All output is readable β€” use `JSON.stringify` with indentation where helpfulCompleted clearly and correctly in your solution.

What's next

Key Takeaways:

  • Explain what JSON is and how it differs from a JavaScript object
  • Use `JSON.parse()` and `JSON.stringify()` correctly
  • Navigate and extract values from nested JSON structures
  • Use `map()`, `filter()`, and `find()` to reshape and query API data
  • Recognise and avoid the most common JSON gotchas

Learning Objectives Review:

Look back at what you set out to learn. Can you now:

  • βœ… Explain what JSON is and how it differs from a JavaScript object Check!
  • βœ… Use `JSON.parse()` and `JSON.stringify()` correctly Got it!
  • βœ… Navigate and extract values from nested JSON structures Can explain it!
  • βœ… Use `map()`, `filter()`, and `find()` to reshape and query API data Could teach this!
  • βœ… Recognise and avoid the most common JSON gotchas Check!

If you can confidently answer "yes" to most of these, you're ready to move on!

Think & Reflect:

πŸ’­ Pause and reflect

  • Which idea from this lesson now feels practical rather than abstract?
  • What would you build or test next to make this stick?

🎯 Looking Ahead:


You can now fetch data, parse it, and reshape it into exactly the form your code needs. The next step is making that code cleaner and easier to read.

async/await is syntactic sugar built on top of Promises β€” the same engine, a different way of writing it. Most modern codebases prefer it because it reads more like synchronous code while staying fully async.

β†’ Next: async/await Patterns

Recommended Next Steps

Continue Learning

Ready to move forward? Continue with the next tutorial in this series:

async/await Patterns

Related Topics

Explore these related tutorials to expand your knowledge:

Progress tracking is disabled. Enable it in to track your completed tutorials.