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.
π― Start Here
APIs don't send you a JavaScript object. They send you text β a long string of characters that looks like an object. Before you can use any of it, JavaScript needs to convert it. That conversion is called parsing, and the format it's parsing is JSON.
Understanding JSON isn't just about knowing its syntax. It's about knowing how to take a raw blob of data from an API β which is almost never in exactly the shape you need β and turn it into something your UI can actually use.
That's what this tutorial covers.
- Where have you already seen working with json in a real interface?
- Which part of working with data currently feels most mysterious?
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.
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 syntax rules
JSON is stricter than JavaScript object notation. A few key differences:
| Rule | JSON | JavaScript object |
|---|---|---|
| Keys | Must be double-quoted strings | Can be unquoted identifiers |
| Strings | Double quotes only | Single or double quotes |
| Trailing commas | Not allowed | Allowed in modern JS |
| Functions | Not allowed | Allowed |
undefined | Not allowed | Allowed |
| Comments | Not allowed | Allowed |
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); // 42When 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.
Navigating nested JSON
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 thrownReshaping 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
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 goneUse 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. What's the key difference between a JSON string and a JavaScript object?
- 2. Why does `JSON.stringify()` drop `undefined` values?
- 3. When would you use `filter()` vs `find()`?
- 4. Why should you use `null` rather than `undefined` in data you're going to serialise?
Check Your Answers
- 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).
- Because `undefined` is not a valid JSON value type. JSON only supports strings, numbers, booleans, null, arrays, and objects.
- 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`).
- `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:
| Criteria | You've succeeded if... |
|---|---|
| The fetch uses the full `response.ok` pattern | Completed clearly and correctly in your solution. |
| Each item in the reshaped array has exactly the four properties listed above | Completed 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 helpful | Completed 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 PatternsRelated Topics
Explore these related tutorials to expand your knowledge: