Modern JavaScript (ES6+)
If you’re already exposed to basic jQuery, modern JavaScript will feel familiar but way more powerful.
🎯 What you’ll learn today:
- Install Node.js and run JavaScript files from terminal
let
,const
instead ofvar
- Arrow functions
() => {}
- Template literals
`Hello ${name}`
- Introduction to destructuring and array methods (map, filter, reduce)
- Basic
fetch()
for API calls - Log structured post data to the console
- Explore JSON data from a WordPress blog
- Render pulled data into dynamic HTML
🛠️ Step-by-Step Plan for Today
- Create a folder:
fullstack-bootcamp/day01
- Inside, make a file:
index.js
- Write and test the following:
// 1. let vs const
let name = "John";
const age = 30;
// 2. Arrow function
const greet = (name) => `Hello, ${name}`;
console.log(greet(name));
// 3. Template literals
console.log(`Your name is ${name} and you're ${age} years old.`);
// 4. Destructuring
const user = { username: "john_doe", email: "john@example.com" };
const { username, email } = user;
console.log(`User: ${username}, Email: ${email}`);
// 5. Array methods
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled);
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens);
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum);
// 6. fetch API (basic)
fetch("https://abandonedpetrescue.org/wp-json/wp/v2/posts")
.then(res => res.json())
.then(posts => {
posts.forEach(post => {
console.log(`Title: ${post.title.rendered}`);
console.log(`Excerpt: ${post.excerpt.rendered}`);
});
})
.catch(err => console.error("Error fetching posts:", err));
Now, install node.js if you haven’t already:
- Install Node.js
- In Command Prompt (CMD), run the file with:
node "C:\fullstack-bootcamp\day01\index.js"
🧱 let
and const
— Declarations (Keywords)
- What they are: These are keywords used to declare variables.
- Not functions or expressions.
- You’re not calling a function or writing an expression — you’re telling JavaScript to reserve a variable using a keyword.
🔑 let
vs const
– What’s the Difference?
Keyword | Can Reassign? | Can Redeclare? | Scope Type | When to Use |
---|---|---|---|---|
let | ✅ Yes | ❌ No | Block | When value might change |
const | ❌ No | ❌ No | Block | When value stays the same |
✅ let
– Use When the Value Can Change
let age = 25;
age = 26; // ✅ Allowed
console.log(age); // 26
🔒 const
– Use When the Value Shouldn’t Change
const name = "Alice";
name = "Bob"; // ❌ Error: Assignment to constant variable
BUT — there’s a catch:
⚠️ const
Doesn’t Mean “Frozen”
If you assign a reference type like an object or array, you can still change its contents:
const user = { name: "Alice" };
user.name = "Bob"; // ✅ This works!
console.log(user); // { name: "Bob" }
You just can’t reassign the user
variable to something completely new.
🧠 Block Scope
Both let
and const
are block-scoped, meaning they only exist inside the {}
they’re declared in.
if (true) {
let secret = "hidden";
const locked = "locked";
}
// secret and locked are NOT accessible here
🔁 Summary:
- Use
let
when you know the value will change (like in loops, counters, etc.) - Use
const
by default (to avoid accidental changes)
💡 What is an Arrow Function?
Arrow functions are a shorter way to write functions in JavaScript.
They were introduced in ES6 (2015) and are mainly used for concise syntax and cleaner code, especially for things like .map()
, .filter()
, or event handlers.
🆚 Traditional Function vs. Arrow Function
// Traditional Function
function add(x, y) {
return x + y;
}
// Arrow Function
const add = (x, y) => x + y;
Both do the exact same thing, but the arrow function is more concise.
📌 Basic Syntax
(parameter1, parameter2) => {
// function body
return something;
}
📦 One-Liner Arrow Function
If the body only has one return statement, you can skip the {}
and return
.
const double = x => x * 2;
That’s the same as:
const double = function(x) {
return x * 2;
}
💬 With Multiple Parameters
const multiply = (a, b) => a * b;
🔂 No Parameters
You still need empty parentheses:
const greet = () => console.log("Hello!");
⚠️ What Arrow Functions Don’t Do
Arrow functions do not bind their own this
(context).
This means they behave differently inside objects or classes compared to regular functions.
const person = {
name: "John",
greet: () => {
console.log(`Hello, I'm ${this.name}`); // ❌ 'this' is not person here
}
};
✅ Where Arrow Functions Shine
- Short one-liner functions
- Callbacks (like
.map()
,.filter()
,.forEach()
) - Functional programming style
💡 What Are Template Literals?
Template literals (or template strings) allow you to embed variables and expressions directly inside strings using backticks (` `) instead of regular quotes (‘ or “).
🔤 Basic Syntax
const name = "John";
const greeting = `Hello ${name}`;
console.log(greeting); // Hello John
Here:
- The backticks ( ` ` ) wrap the string.
- The
${...}
syntax lets you drop in any variable or expression.
🔁 Compare to Old Style
Before (concatenation):
const name = "John";
const greeting = "Hello " + name;
Now (template literal):
const greeting = `Hello ${name}`;
Cleaner, easier to read, and less error-prone.
🧮 You Can Use Expressions Too
const a = 5;
const b = 10;
console.log(`The sum is ${a + b}`); // The sum is 15
📄 Multi-Line Strings (Another Superpower)
With template literals, you can write multi-line strings without using \n
.
const poem = `Roses are red,
Violets are blue,
JavaScript is awesome,
And so are you.`;
console.log(poem);
✅ Best Use Cases
- Embedding values in strings
- Creating dynamic content for the DOM
- Writing readable multi-line strings
- Avoiding messy string concatenation
Template literals don’t introduce new capabilities, but they make your life way easier when writing strings that include:
- Variables
- Expressions
- Multi-line content
They’re a syntax upgrade, not a functionality upgrade — meaning:
- ✅ Your code is shorter
- ✅ It’s easier to read and maintain
- ❌ But you’re not doing anything you couldn’t do before with regular strings and concatenation
Think of it like switching from writing with a pen to typing on a keyboard — same output, just way more efficient.
🧨Destructuring
1. Introduction to Destructuring
Destructuring is a powerful feature in JavaScript that allows you to unpack values from arrays or objects into individual variables. It simplifies extracting values from data structures.
💡 Array Destructuring
const numbers = [10, 20, 30];
// Traditional method
const first = numbers[0];
const second = numbers[1];
// Destructuring method (shortcut)
const [first, second] = numbers;
console.log(first); // 10
console.log(second); // 20
💡 Object Destructuring
You can also destructure objects.
const user = {
name: "John",
age: 30,
city: "New York"
};
// Traditional method
const name = user.name;
const age = user.age;
// Destructuring method (shortcut)
const { name, age, city } = user;
console.log(name); // John
console.log(age); // 30
console.log(city); // New York
🛠 Why Use Destructuring?
- Makes your code cleaner and reduces repetitive access to properties.
- Helps when dealing with complex data (especially from APIs).
- Ideal for function parameters when passing objects or arrays.
2. Array Methods
Now, let’s focus on three very important array methods in JavaScript: .map()
, .filter()
, and .reduce()
.
💡 .map()
The .map()
method transforms every element in an array and returns a new array. It doesn’t mutate the original array.
const numbers = [1, 2, 3, 4, 5];
// Doubling each number using map
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
Use case: When you want to transform each item in an array.
💡 .filter()
The .filter()
method creates a new array with all elements that pass a test (provided by a callback function).
const numbers = [1, 2, 3, 4, 5];
// Filtering even numbers
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
Use case: When you want to filter out elements based on a condition.
💡 .reduce()
The .reduce()
method is a bit more advanced. It accumulates values from an array into a single value (e.g., sum, product, etc.).
const numbers = [1, 2, 3, 4, 5];
// Summing up the numbers using reduce
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 15
- Use case: When you need to combine or accumulate the array elements into a single result.
The parameters for .reduce()
:
- Accumulator: This accumulates the result.
- Current Value: The current element being processed.
- The initial value: (in our example, it’s
0
).
3. Practical Example with Destructuring & Array Methods
Let’s combine these concepts to create a more complete example.
const users = [
{ name: "John", age: 25, city: "New York" },
{ name: "Jane", age: 30, city: "London" },
{ name: "Sara", age: 28, city: "Paris" },
{ name: "Mike", age: 35, city: "New York" }
];
// Destructuring within map to extract data
const userInfo = users.map(({ name, age }) => `${name} is ${age} years old`);
console.log(userInfo); // ["John is 25 years old", "Jane is 30 years old", ...]
// Filtering users from New York
const newYorkUsers = users.filter(({ city }) => city === "New York");
console.log(newYorkUsers);
// [ { name: "John", age: 25, city: "New York" }, { name: "Mike", age: 35, city: "New York" } ]
// Summing up ages of all users
const totalAge = users.reduce((acc, { age }) => acc + age, 0);
console.log(totalAge); // 118
4. Key Takeaways
- Destructuring: Makes accessing elements inside arrays and objects simpler.
.map()
: Transforms each item in an array and returns a new array..filter()
: Filters items based on a condition and returns a new array..reduce()
: Reduces an array to a single value (e.g., sum, product, etc.).
These array methods and destructuring allow for cleaner, more expressive, and functional code. They’re a great foundation for writing modern JavaScript applications, especially in tasks like manipulating data from APIs, UI updates, and more.
🐕Fetch
In JavaScript, the fetch()
method is used to make network requests to retrieve data from APIs or other resources over the internet. It’s a built-in browser function that returns a Promise and allows us to asynchronously get resources.
1. Basic Fetch Syntax
Here’s a simple example of how fetch()
is used to make a GET request to an API:
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json()) // Convert the response to JSON
.then(data => console.log(data)) // Handle the data from the API
.catch(error => console.log('Error:', error)); // Catch any errors
Breaking it Down
fetch()
takes a URL (string) as its first parameter. This is the endpoint you want to fetch data from.- The
response
object returned byfetch()
represents the response to the request, but it’s not directly usable yet — you need to parse it. response.json()
converts the response body from JSON to a JavaScript object (this returns a Promise).- The second
.then()
function is used to handle the data you receive, and you can do something with it (e.g., logging, rendering to the page). .catch()
is used for error handling if something goes wrong with the request.
2. Example – Fetching Data from an API
Let’s fetch some posts from the jsonplaceholder
API, a fake online REST API for testing and prototyping.
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json()) // Parse the JSON from the response
.then(posts => {
console.log('Fetched posts:', posts); // Display the posts in the console
})
.catch(error => console.log('Error fetching posts:', error)); // Handle any errors
This will print the posts data in the console. The data looks something like this:
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
...
]
3. Fetching Data and Displaying it in HTML
You can take the data you fetch and display it on a web page. Here’s an example where we fetch posts and display the titles in an HTML page.
HTML Structure
<div id="posts-container"></div>
JavaScript to Fetch and Display
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json()) // Parse the JSON from the response
.then(posts => {
const postsContainer = document.getElementById('posts-container');
posts.forEach(post => {
const postElement = document.createElement('div');
postElement.classList.add('post');
postElement.innerHTML = `<h3>${post.title}</h3><p>${post.body}</p>`;
postsContainer.appendChild(postElement);
});
})
.catch(error => console.log('Error fetching posts:', error)); // Handle any errors
4. Making POST Requests with fetch()
While fetch()
is mostly used for GET requests, you can also use it to send POST requests, which are commonly used to send data to a server.
Here’s an example of how to send a POST request:
const postData = {
title: 'foo',
body: 'bar',
userId: 1
};
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST', // Specify the request method (POST)
headers: {
'Content-Type': 'application/json' // Set the content type
},
body: JSON.stringify(postData) // Convert the JavaScript object to JSON
})
.then(response => response.json()) // Parse the response JSON
.then(data => console.log('Data sent:', data)) // Handle the response data
.catch(error => console.log('Error posting data:', error)); // Handle any errors
method
: Set to'POST'
to indicate we’re sending data.headers
: Specify that the content we’re sending is in JSON format.body
: The data we want to send, converted into a JSON string.
5. Fetch Options (Second Parameter)
fetch()
accepts an optional second parameter that allows you to customize the request. This is useful for things like sending data or adding authentication.
Example with GET (default):
fetch('https://jsonplaceholder.typicode.com/posts') // Only the URL is needed
.then(response => response.json())
.then(data => console.log(data));
Example with POST (using second parameter):
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST', // Specify the method
headers: {
'Content-Type': 'application/json' // We are sending JSON data
},
body: JSON.stringify({ title: 'Hello', body: 'World' }) // Data to send
})
.then(response => response.json())
.then(data => console.log(data));
Common fetch()
Options:
method
: The HTTP request method (GET, POST, PUT, DELETE, etc.).headers
: Specify headers (e.g.,'Content-Type': 'application/json'
).body
: For sending data in a POST or PUT request, usebody
to send the data (usually in JSON format).mode
: You can specify the mode (e.g.,'cors'
,'no-cors'
,'same-origin'
).credentials
: Include credentials like cookies ('same-origin'
,'include'
).
6. Handling Errors
When using fetch()
, the promise is rejected only if there’s a network error (e.g., no internet). It does not reject for HTTP errors (e.g., 404 or 500). You’ll need to manually check the response.ok
property to detect these.
Here’s an example of how to handle HTTP errors:
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // Only parse if the response is okay
})
.then(data => console.log(data))
.catch(error => console.log('There was a problem with the fetch operation:', error));
response.ok
is true for successful responses (status code 200-299).response.ok
is false for errors (status code 400 and above).
Summary of fetch()
fetch()
is used to make network requests and get data from APIs.- By default,
fetch()
makes a GET request. - You can handle the response with
.then()
and parse it to JSON usingresponse.json()
. - POST requests send data to an API, and you can customize
fetch()
with a second parameter to set things like HTTP methods, headers, and body data. - Errors can be handled using
.catch()
, but you need to manually checkresponse.ok
for HTTP errors.
Now you can use fetch()
to interact with APIs, get data from external resources, and send data back to servers!
📜Logging Structured Post Data to the Console
When interacting with APIs, especially when dealing with post data, it’s often useful to log and inspect the data in the console. This helps us verify the data we’ve received and understand its structure before manipulating or displaying it.
In this section, we’ll go over how to log structured post data to the console, using JavaScript to fetch the data from an API and display it in a readable way.
1. Fetch Data from an API
Let’s start by fetching post data from an API. We’ll use the same JSONPlaceholder API (a mock API for testing) that provides dummy posts. This API returns an array of post objects with properties like id
, title
, body
, etc.
Here’s a simple example to fetch posts and log the response to the console:
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json()) // Parse the response as JSON
.then(posts => {
console.log(posts); // Log all posts
})
.catch(error => console.log('Error:', error)); // Handle any errors
2. What Does This Log?
The log in the console will display an array of objects (posts), each containing information such as:
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat sit in est est\nvoluptatem omnis possimus esse voluptatibus quis"
}
...
]
3. Accessing Specific Post Data
If you only want to log specific post data (e.g., just the title and body of each post), you can use forEach
or other array methods to loop through the data and print out specific fields.
Example: Log Title and Body for Each Post
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(posts => {
posts.forEach(post => {
console.log(`Post ID: ${post.id}`);
console.log(`Title: ${post.title}`);
console.log(`Body: ${post.body}`);
console.log('------------------------');
});
})
.catch(error => console.log('Error:', error));
4. Structured Logging
To make the data even more structured and easier to read, you can use template literals and format the output neatly.
Example: Structured Logging with Template Literals
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(posts => {
posts.forEach(post => {
console.log(`-------------------------`);
console.log(`Post ID: ${post.id}`);
console.log(`Title: ${post.title}`);
console.log(`Body: ${post.body}`);
console.log(`User ID: ${post.userId}`);
console.log(`-------------------------`);
});
})
.catch(error => console.log('Error:', error));
This gives a clean, structured output, making it easier to see the data associated with each post.
5. Example of Logging Post Data in JSON Format
If you want to log the entire post object as a JSON string (useful for debugging), you can use JSON.stringify()
to convert the object to a string. This will give you a clear view of all the properties of the object.
Example: Log Each Post as JSON
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(posts => {
posts.forEach(post => {
console.log(JSON.stringify(post, null, 2)); // Pretty-printing with indentation
});
})
.catch(error => console.log('Error:', error));
6. Logging More Complex Structured Data
Sometimes, the data you work with will be more complex, such as a post that has nested data (e.g., comments or media). In this case, you can log the structured data in a way that shows the full hierarchy.
Example: Post with Nested Comments
Let’s assume the API response includes comments. Here’s an example of how you might log this more complex data:
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(post => {
console.log(`Post ID: ${post.id}`);
console.log(`Title: ${post.title}`);
console.log(`Body: ${post.body}`);
console.log('Comments:');
post.comments.forEach(comment => {
console.log(` Comment ID: ${comment.id}`);
console.log(` Name: ${comment.name}`);
console.log(` Body: ${comment.body}`);
});
})
.catch(error => console.log('Error:', error));
7. Summary of Logging Post Data
To summarize, here’s a list of key steps when logging structured post data to the console:
- Use
fetch()
to get data from an API. - Parse the response into JSON with
.json()
. - Use
.forEach()
or similar array methods to iterate over the posts. - Access specific data fields and log them in a readable format using template literals.
- Log entire post objects using
JSON.stringify()
for more detailed debugging. - Log nested data by iterating over sub-arrays (like comments or media).
This practice of logging data is essential when working with APIs, as it helps you understand the structure of the response and verify that you’re accessing the correct data.
🧭 Explore JSON Data from a WordPress Blog
WordPress powers a huge number of websites, and many of them expose their content through a built-in REST API. This means we can use JavaScript’s fetch()
method to pull in blog posts, pages, and other data — in real-time — directly from the site.
In this lesson, we’ll explore how to fetch live blog post data from a real WordPress site using their JSON endpoint.
API Endpoint We’re Using
We’re pulling blog post data from this real-world WordPress API:
https://abandonedpetrescue.org/wp-json/wp/v2/posts
This endpoint returns a list of recent blog posts in JSON format — each with details like the title, date, author, and content.
Step-by-Step: Fetch and Log the Blog Post Data
fetch('https://abandonedpetrescue.org/wp-json/wp/v2/posts')
.then(response => response.json())
.then(posts => {
posts.forEach(post => {
console.log('--------------------------');
console.log(`Post ID: ${post.id}`);
console.log(`Title: ${post.title.rendered}`);
console.log(`Date: ${post.date}`);
console.log(`Excerpt: ${post.excerpt.rendered}`);
console.log('--------------------------');
});
})
.catch(error => console.error('Error fetching posts:', error));
What’s Going On Here?
fetch(...)
: Grabs the data from the WordPress JSON API..then(response => response.json())
: Converts the response into usable JSON..forEach(post => { ... })
: Loops through each blog post.post.title.rendered
: WordPress wraps some fields (like titles) in a.rendered
property, especially if they contain HTML.
Output Preview
You’ll see something like this in the console:
--------------------------
Post ID: 3026
Title: Adopting Tiny the Chihuahua
Date: 2024-12-15T14:30:22
Excerpt: <p>Meet Tiny, a sweet little soul who...</p>
--------------------------
(Note: The actual content may change depending on the site’s latest posts.)
Next Steps You Can Try
- Render the titles into your HTML page instead of just logging to the console.
- Use
.innerHTML
to displaypost.title.rendered
orpost.excerpt.rendered
directly. - Create clickable links using
post.link
. - Format the date using
new Date(post.date).toLocaleDateString()
for nicer display.
🖼️ Render Pulled Data into Dynamic HTML
Now that we’ve successfully fetched post data from a WordPress site using the REST API, let’s take it one step further — display the data right on the webpage.
Instead of just logging info to the console, we’ll create dynamic HTML elements and inject them into the DOM. This is how you turn API data into actual content users can see and interact with.
What You’ll Learn in This Step
- Create elements in JavaScript (
document.createElement
) - Add content to them (
.innerHTML
) - Attach them to the page (
appendChild
) - Loop through an array of posts and dynamically generate a list
HTML Structure
You’ll need a simple HTML container where the posts will go:
<body>
<style>
body {
font-family: sans-serif;
background: #f4f4f4;
padding: 2rem;
}
.post {
background: #fff;
padding: 1rem;
margin-bottom: 2rem;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.post img {
max-width: 100%;
border-radius: 4px;
}
.post h2 {
margin-top: 0;
}
.post .meta {
color: #555;
font-size: 0.9em;
}
</style>
<h1>Latest Blog Posts 🐾</h1>
<div id="posts-container"></div>
<script>
const container = document.getElementById('posts-container');
fetch('https://abandonedpetrescue.org/wp-json/wp/v2/posts')
.then(res => res.json())
.then(posts => {
posts.forEach(post => {
const postElement = document.createElement('div');
postElement.classList.add('post');
postElement.innerHTML = `
<h2>${post.title.rendered}</h2>
<p><em>Published on ${new Date(post.date).toLocaleDateString()}</em></p>
<div>${post.excerpt.rendered}</div>
<a href="${post.link}" target="_blank">Read more →</a>
`;
container.appendChild(postElement);
});
})
.catch(err => console.error('Error fetching posts:', err));
</script>
</body>
What’s Happening Here?
- We select the container
<div>
where our posts will go. - We loop through each post with
forEach()
. - For each post, we:
- Create a new
<div>
- Insert the title, date, excerpt, and a link
- Append that element to the page
- Create a new
- The result: a live, dynamic list of blog posts that updates whenever the WordPress site updates.
Add Featured Image and Author
Because the Featured Image and Author are stored on another URL, we have to perform additional jumps and bring their data back. To handle this, we can use Promise
.

We’ll build on the previous code to:
- Fetch each post ✅
- Get the featured image URL using the
featured_media
ID 🖼 - Get the author name using the
author
ID 👤 - Combine it all into a clean HTML layout
Updated script.js Example
const container = document.getElementById('posts-container');
fetch('https://abandonedpetrescue.org/wp-json/wp/v2/posts')
.then(res => res.json())
.then(posts => {
posts.forEach(post => {
// Fetch the featured image
const mediaPromise = post.featured_media
? fetch(`https://abandonedpetrescue.org/wp-json/wp/v2/media/${post.featured_media}`).then(res => res.json())
: Promise.resolve(null);
// Fetch the author name
const authorPromise = fetch(`https://abandonedpetrescue.org/wp-json/wp/v2/users/${post.author}`).then(res => res.json());
// Once both media and author are ready, render the post
Promise.all([mediaPromise, authorPromise]).then(([media, author]) => {
const postElement = document.createElement('div');
postElement.classList.add('post');
const imageHtml = media ? `<img src="${media.source_url}" alt="Featured image" class="featured-image">` : '';
const authorName = author?.name || 'Unknown';
postElement.innerHTML = `
${imageHtml}
<h2>${post.title.rendered}</h2>
<p><em>By ${authorName} • ${new Date(post.date).toLocaleDateString()}</em></p>
<div>${post.excerpt.rendered}</div>
<a href="${post.link}" target="_blank">Read more →</a>
`;
container.appendChild(postElement);
});
});
})
.catch(err => console.error('Error fetching posts:', err));
Rendering:
- ✅ Post Title
- ✅ Excerpt
- ✅ Publish Date
- ✅ Author Name
- ✅ Featured Image
- ✅ “Read more” link
Why Promise.all()
?
Because we’re making two separate async calls:
- One to get the featured image (
media
) - One to get the author info (
author
)
We need to wait for both to finish before rendering the post. That’s where this comes in:
Promise.all([mediaPromise, authorPromise]).then(([media, author]) => {
// Now we have both!
});
This ensures:
- The post won’t be shown until we’ve received both pieces of info.
- Each post can be fetched and displayed independently of others (super efficient).
- We use
Promise
because network requests are async. Promise.all()
waits for both media and author data before continuing.- It keeps our app fast, non-blocking, and clean.
Advantages of the async/await
Version
1. Cleaner and Easier to Read
It reads top-to-bottom like regular code, making it easier to follow than chained .then()
methods or nested Promise.all()
calls.
2. Sequential Control
Each await
lets you pause execution until a specific fetch is complete. This is useful when you want:
- Author info before rendering the post
- Featured image before including it
3. Error Handling with Try/Catch
You can easily handle API errors and fallback gracefully (like defaulting to "Unknown"
for authors or skipping broken image fetches).
Potential Trade-Offs
1. Sequential Requests
This code runs fetches one-by-one (for each post: fetch post → fetch author → fetch media), which can be slower compared to firing off all author/media fetches in parallel using Promise.all
.
For 10+ posts, it’s a noticeable delay. For a smaller blog, the difference is minor.
2. No Parallel Fetching of Author & Media
In the previous Promise.all
version, author and media were fetched at the same time per post. If speed is crucial, you could do:
const [authorData, mediaData] = await Promise.all([authorFetch, mediaFetch]);
But this adds complexity.
When to use async/await
vs Promise.all
Using the async/await
version is great for clarity and learning, and perfectly fine for smaller projects. If performance ever becomes a concern (like loading 50+ posts), then optimizing with Promise.all
might be worth revisiting.
✨Conclusion: What Was Learned
Learning Recap: JavaScript Fundamentals to Dynamic API Rendering
Over the past lessons, you’ve built a solid foundation in modern JavaScript—starting with the basics and steadily progressing into real-world, dynamic web functionality. You began by installing Node.js and getting familiar with variables using let
and const
, learning when to use each based on whether a value will change. From there, you explored arrow functions—a concise way to write functions—and template literals, which made string handling more readable and expressive using ${}
placeholders.
You then dove into powerful ES6+ features like destructuring, which lets you extract values directly from arrays or objects, and array methods like map()
, filter()
, and reduce()
. These are essential for transforming data and working with lists efficiently. For example, you filtered even numbers from an array by checking if there’s no remainder using num % 2 === 0
, which taught you the logic behind modular arithmetic and boolean conditions.
As you progressed, you unlocked the ability to make API requests using fetch()
—starting with simple calls, logging data to the console, and eventually parsing JSON responses from real WordPress blogs. You practiced rendering this data into dynamic HTML, and added advanced touches like grabbing featured images, author names, categories, and tags—even making them clickable links to their respective archive pages.
To manage the complexity of multiple asynchronous data requests, you were introduced to Promises, then upgraded to the more modern and readable async/await
syntax. This allowed you to pause and resume logic as needed, making your code both more powerful and easier to follow.
This journey took you from raw data to rich, styled, interactive content—all with vanilla JavaScript. You’ve now got a working example that fetches real-time blog posts, complete with metadata, and displays them in a clean, readable format—setting the stage for deeper front-end development or even integrating into frameworks later. 💻🚀
This roadmap was developed using a combination of hands-on coding, API exploration, and help from AI-powered tools like ChatGPT.