Fetch API Calls
Instructor-Only
Section titled “Instructor-Only”
Estimated walkthrough time (with discussion): 85-110 minutes
Instructor-Only
Section titled “Instructor-Only”Estimated walkthrough time (with discussion): 85-110 minutes
Estimated walkthrough time (with discussion): 85-110 minutes
⚠️ Be mindful of delivery time; this walkthrough can easily run long if discussion expands.
Rationale:
Section titled “Rationale:”- Lecture: 25-35 minutes
- Demo Walkthrough: 60-75 minutes
⚠️ Be mindful of delivery time; this walkthrough can easily run long if discussion expands.
- This lesson is longer than lesson 02 and adds asynchronous browser-to-API work.
- Students type through running two servers, smoke-testing the API, building fetch helpers, rendering app data, and handling common errors.
- The walkthrough needs time for JSON parsing, async flow, and checking both browser and API server output.
- The estimate allows extra time for slower typing and troubleshooting two-process development setup.
Lecture
Section titled “Lecture”The previous lesson gave students a local REST-style API that we tested by making requests from a *.http file. This lesson moves the request into the browser so JavaScript can ask for server-side data and update the page when a response arrives.
Asynchronous Requests
Section titled “Asynchronous Requests”HTTP requests take time. The browser sends a request, the server works on it, and the response arrives later. JavaScript cannot treat that response as an immediate value.
This is why fetch() returns a promise. A promise represents work that may finish later:
.then()runs when the promise resolves..catch()runs when the promise rejects.asyncfunctions let code useawaitto pause inside the function until a promise settles.
Fetch and JSON
Section titled “Fetch and JSON”The Fetch API returns a Response object first. That response object contains status information and methods for reading the body.
For JSON APIs, the usual flow is:
fetch(endpoint)- Check
response.ok - Call
response.json() - Render the parsed JavaScript data into the DOM
Advanced DevTools Features
Section titled “Advanced DevTools Features”- Network Panel for checking API calls
- Application Panel for local storage, session storage, and cookies
- Lighthouse Panel for performance
- Memory Panel
Demo Walkthrough
Section titled “Demo Walkthrough”This demo runs two local processes at the same time: JSON Server for the backend API and Vite for the frontend page. The concurrently package lets students run both with one command.
Starter Files
Section titled “Starter Files”Directorydemo
Directory.vscode
- extensions.json
Directorycss
- styles.css
Directoryjs
- main.js
- utils.js
Directorypublic
- earth.jpg
- sun.jpg
- …
- api-smoke-test.http
- db.json
- index.html
- package.json
- ReadMe.md
Install and Run Both Servers
Section titled “Install and Run Both Servers”-
Open the
~/inclass/19/demofolder in VS Code. -
Install dependencies.
Terminal pnpm install -
Start the frontend and backend together.
Terminal pnpm dev -
Confirm the terminal shows both
frontendandapi-serveroutput. -
Open the Vite URL in the browser.
The important package scripts are:
{ "scripts": { "frontend": "vite", "api-server": "json-server --watch db.json --port 3000", "dev": "concurrently -n frontend,api-server -c blue,green \"pnpm run frontend\" \"pnpm run api-server\"" }}Smoke-Test the API
Section titled “Smoke-Test the API”Before writing browser JavaScript, prove that the backend API works.
-
Install the VS Code REST Client extension if VS Code recommends it.
-
Open
api-smoke-test.http. -
Send the all-bodies request.
api-smoke-test.http ### GET all bodiesGET http://localhost:3000/bodiesAccept: application/json -
Send the single-body request.
api-smoke-test.http ### GET one bodyGET http://localhost:3000/bodies/1Accept: application/json -
Confirm both responses return JSON.
Build Utility Functions
Section titled “Build Utility Functions”Keep API details in utils.js so main.js can focus on the page behavior.
-
Create a helper that fetches JSON data.
js/utils.js export function fetchJsonData(endpoint) {return fetch(endpoint).then(handleResponse);} -
Add a shared response handler.
js/utils.js function handleResponse(response) {if (!response.ok) {const message = `Server responded with ${response.status} (${response.statusText}).`;throw new Error(message);}return response.json();} -
Add a POST helper using
asyncandawait.js/utils.js export async function postData(endpoint, payload) {const response = await fetch(endpoint, {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify(payload),});return handleResponse(response);}
This deliberately shows both styles: promise chaining in fetchJsonData() and async/await in postData().
Build the App
Section titled “Build the App”-
Import the helper functions.
js/main.js import { fetchJsonData, postData } from './utils.js'; -
Select the DOM elements and define the endpoint.
js/main.js const loadButton = document.querySelector('#load-bodies');const addForm = document.querySelector('#add-body');const bodyList = document.querySelector('#body-list');const statusMessage = document.querySelector('#status-message');const endpoint = 'http://localhost:3000/bodies'; -
Create small rendering helpers for status text and the body list.
js/main.js function showStatus(message, type = 'info') {statusMessage.textContent = message;statusMessage.dataset.status = type;}function renderBodies(bodies) {bodyList.replaceChildren();bodies.forEach((body) => {const item = document.createElement('li');const image = document.createElement('img');image.src = `http://localhost:3000/${body.image}`;image.alt = body.name;const text = document.createElement('span');text.textContent = `${body.name} - ${body.category} - ${body.orbitalPeriod}`;item.append(image, text);bodyList.append(item);});} -
Add the load handler.
js/main.js function loadHandler() {showStatus('Loading bodies...');bodyList.innerHTML = '<li>Loading...</li>';fetchJsonData(endpoint).then((bodies) => {renderBodies(bodies);showStatus(`Loaded ${bodies.length} bodies.`, 'success');}).catch((error) => {bodyList.replaceChildren();showStatus(`Error: ${error.message}`, 'error');});} -
Add the submit handler.
js/main.js async function submitHandler(event) {event.preventDefault();const formData = new FormData(event.target);const body = Object.fromEntries(formData.entries());try {await postData(endpoint, body);event.target.reset();showStatus('Body added.', 'success');loadHandler();} catch (error) {showStatus(`Error: ${error.message}`, 'error');}} -
Attach the event listeners.
js/main.js loadButton.addEventListener('click', loadHandler);addForm.addEventListener('submit', submitHandler);
Optional Student Exercise
Section titled “Optional Student Exercise”Extend the example by adding DELETE functionality. Add a delete button to each rendered body, create a deleteData() helper, and refresh the list after the server confirms the item was removed.
Common Errors and Fixes
Section titled “Common Errors and Fixes”| Issue | Likely Cause | Fix |
|---|---|---|
Failed to fetch | JSON Server is not running | Run pnpm dev and check the api-server output |
| CORS or module error | Page opened with file:// | Use the Vite URL |
| Empty list | API returned an empty array | Check db.json and smoke-test the endpoint |
| POST returns an error | JSON body or headers are wrong | Check Content-Type and JSON.stringify() |
| Form reloads the page | Missing preventDefault() | Add event.preventDefault() in the submit handler |
Conclusion
Section titled “Conclusion”Assigned homework: Complete Homework: Practice Fetch Helpers. It reinforces today’s Fetch API workflow and gives fallback practice for helper functions, JSON parsing, rendering, loading states, and async/API errors.
By the end of this demo, students should understand the path from a browser event to an API request and back to a DOM update. fetch() starts the request, promises manage the waiting, JSON turns the response into JavaScript data, and DOM methods make that data visible to the user.