W03 Learning Activity: Using Web Services to Dynamically Hydrate HTML Pages
Overview
- Purpose: Learn how web services can dynamically hydrate HTML pages.
- Task: Read the following information and complete the activity instructions below.
In the existing team project code, the pages are rendered on the server. A route handler gathers data, passes it to an EJS template, and the server sends finished HTML to the browser.
This week, your team will be implementing a MongoDB database and introduce a new, different pattern: the server exposes JSON API endpoints, and frontend JavaScript asks those endpoints for data. The browser receives JSON, builds the needed HTML, and updates the page without needing the server to render every piece of content.
From your experience in WDD 330, you should be familiar with the process of using client-side JavaScript to make requests and update web pages dynamically. This learning activity demonstrates how these concepts connect with the new Web Services approach. If you need to review the concepts of the previous course, refer to the WDD 330 course materials.
Preparation Material
Server-rendered pages versus API-driven pages
| Server-rendered page | API-driven page |
|---|---|
The Express route calls res.render(). |
The Express route returns JSON with res.json(). |
| EJS loops over data while the page is being generated. | Browser JavaScript loops over JSON after the page loads. |
| The browser receives mostly finished HTML. | The browser receives data and builds part of the interface. |
Benefits of web-service-based page updates
When a page uses a web service, JavaScript can request new data and update only the section of the page that needs to change. The browser does not need to reload the entire page, so the header, navigation, and other unchanged content stay in place while the updated results are rendered.
This is especially helpful when a user's action changes what data is displayed. For example, searching, filtering, and paging through results can be updated without a full page reload. This creates a more responsive and engaging user experience and reduces the amount of data that needs to be transferred.
Your complete application does not need to become a single-page application to gain these benefits. A traditional Express application can still use normal routes and server-rendered EJS pages for most navigation. Then, you can add web-service-based components where a small part of a page needs to respond quickly to a user action or display refreshed data.
The fetch flow
Frontend JavaScript usually uses fetch() to call an API endpoint.
const response = await fetch('/api/products');
const products = await response.json();
The first line sends a request to the API. The second line reads the JSON response body and converts it into normal JavaScript data.
Check the response status
fetch() only rejects for network-level failures. A 404 or 500 response can still produce a response object. Check response.ok before assuming the request succeeded.
const loadProducts = async () => {
const response = await fetch('/api/products');
if (!response.ok) {
throw new Error('Unable to load products.');
}
return response.json();
};
Rendering API data with innerHTML
One common way to render a small amount of API data is to build an HTML string and assign it to a container's innerHTML.
const renderProducts = (products) => {
const productList = document.querySelector('#product-list');
productList.innerHTML = products
.map((product) => `
<article class="product-card">
<h3>${product.name}</h3>
<p>${product.category}</p>
</article>
`)
.join('');
};
This approach is short and can be useful for simple, trusted data. However, the page structure is embedded in a JavaScript string, and API values inserted into the string can be treated as HTML. For larger or reusable page elements, an HTML template keeps the structure in the HTML document and lets JavaScript set values more deliberately.
Hydrating a page with the HTML template element
Hydrating a page means taking data returned by a web service and using it to fill in the page's HTML. The HTML <template> element is a useful starting point for this work. It holds a reusable piece of markup that is not displayed until JavaScript clones it and adds it to the page.
For example, place one product card template and an empty container in the page's HTML. The template describes the structure of every card; it does not contain data for a particular product yet.
<section id="product-list" aria-live="polite"></section>
<template id="product-card-template">
<article class="product-card">
<h3 class="product-name"></h3>
<p class="product-category"></p>
<p class="product-price"></p>
</article>
</template>
After fetch() returns the product data, clone the template once for each product. Set content with textContent instead of building an HTML string. This keeps the markup in the HTML file, makes the template easier to inspect, and avoids treating API text as HTML.
const renderProducts = (products) => {
const productList = document.querySelector('#product-list');
const template = document.querySelector('#product-card-template');
const fragment = document.createDocumentFragment();
products.forEach((product) => {
const productCard = template.content.cloneNode(true);
productCard.querySelector('.product-name').textContent = product.name;
productCard.querySelector('.product-category').textContent = product.category;
productCard.querySelector('.product-price').textContent = `$${product.price}`;
fragment.append(productCard);
});
productList.replaceChildren(fragment);
};
The browser keeps the template content separate from the live page until cloneNode(true) creates a copy. The true value includes all of the template's nested elements. A DocumentFragment collects every completed card, and replaceChildren() replaces the previous page content with the newly hydrated cards.
Keep structure and data separate
Use the template for stable HTML structure and use JavaScript to place API values into named elements. If a product needs an image, link, price, or availability status, add an appropriately named element or attribute to the template and set that value on the cloned card before appending it.
Activity Instructions
Check your understanding
- What is the difference between a server-rendered page and an API-driven page?
Click to expand the example answer
A server-rendered page receives HTML that the server has already built, often with
res.render(). An API-driven page receives data, usually JSON, and browser JavaScript uses that data to render or update part of the page. - Why should you check
response.okafter usingfetch()?Click to expand the example answer
fetch()can resolve even when the server responds with an HTTP error such as404or500. Checkingresponse.oklets your code recognize the unsuccessful response and show an appropriate error state. - What does it mean to hydrate an HTML template?
Click to expand the example answer
Hydrating a template means cloning its reusable HTML structure and filling the clone with values returned by a web service before adding it to the live page.
- Why might you choose an HTML
<template>element instead of building all of your markup withinnerHTML?Click to expand the example answer
A template keeps the page structure in HTML, where it is easier to read and maintain. JavaScript can then use
textContentand attributes to set individual values rather than treating API values as part of an HTML string. - How could a web service improve a product-list page when a user applies a filter or moves to the next page of results?
Click to expand the example answer
JavaScript can request the filtered or next set of products from the API, then replace only the product-list section. The rest of the page remains visible, so the user does not need to wait for a full page refresh.
Deliverables Checklist
- You can explain the difference between server-rendered pages and API-driven pages.
- You can use
fetch()to request JSON from an API. - You can explain why
response.okmatters. - You can explain how an HTML
<template>element can be cloned and hydrated with API data. - You can identify loading, success, and error states for an API-driven page.
- You traced one existing team project page and sketched how it could consume a JSON API.
Other Links:
- Return to: Week Overview | Course Home