JS Async + Fetch

Foundations · Prereqs: F-JS-004
--- theme: seriph title: "JS Async + Fetch" info: "Module F-JS-005 — fetch, await, JSON, request/response shape, basic error handling with try/catch" --- # JS Async + Fetch --- layout: center --- # What Is an API? --- # API = Application Programming Interface **A way for programs to talk to each other** Think of it like a restaurant: - **Menu** (API Documentation) -- shows what you can order - **Waiter** (API) -- takes your request, brings back food - **Kitchen** (Server) -- prepares what you ordered You don't need to know how the kitchen works! --- # How APIs Work ``` Your Website API Server | | |--- Request: "towns?" --->| | | |<-- Response: [{...}] ----| | | ``` 1. You **request** data from a URL 2. Server **processes** the request 3. Server **responds** with data (usually JSON) --- layout: center --- # JSON: The Language of APIs --- # What Is JSON? **JSON** = JavaScript Object Notation. A text format for structured data. ```json { "name": "Alice", "age": 20, "courses": ["OIM3690", "OIM3640"], "active": true } ``` - Keys must be in **double quotes** - Values can be: strings, numbers, booleans, arrays, objects, `null` - Looks like JavaScript objects, works everywhere --- # Nested JSON APIs often return objects inside arrays inside objects: ```json { "name": "Massachusetts", "data": [ { "name": "Boston", "county": "Suffolk", "population": 675647 }, { "name": "Worcester", "county": "Worcester", "population": 206518 } ] } ``` You navigate with dot notation and loops: `data.data[0].name` --- layout: center --- # What Does "Async" Mean? --- # Synchronous vs Asynchronous **Synchronous** -- each step waits for the previous one to finish: ``` Make coffee [====] Toast bread [====] Eat breakfast [====] ``` **Asynchronous** -- start tasks and handle them when they're ready: ``` Make coffee [====] Toast bread [====] Eat breakfast [====] ``` Fetching data from a server takes time (network delay). JavaScript doesn't freeze the page while waiting -- it works **asynchronously**. --- # Why We Need `await` `fetch()` does not give you data immediately. It gives you a **Promise**, a placeholder for data that has not arrived yet: ```js const response = fetch('https://oim.zhili.dev/words/random'); console.log(response); // Promise { <pending> } -- NOT the data! ``` The server needs time to respond. `await` tells JavaScript: **"wait here until the data arrives."** > AI tip: If AI gives you fetch code without `await`, the data will be a Promise object instead of real data. Always check for `await`. --- # Two `await` Calls Every fetch needs two `await` steps: ```js const response = await fetch('https://oim.zhili.dev/words/random'); const word = await response.json(); console.log(word); // "walked" -- actual data! ``` Two `await` calls: 1. `await fetch(...)` -- wait for the server to respond 2. `await response.json()` -- wait for the body to be parsed as JSON --- # `async` Functions `await` can only be used inside an `async` function: ```js async function getRandomWord() { const response = await fetch('https://oim.zhili.dev/words/random'); const word = await response.json(); document.querySelector('#result').textContent = word; } getRandomWord(); ``` - **`async`** before function -- marks it as asynchronous - **`await`** inside the function -- pauses until data arrives > AI tip: Ask AI "write an async function that fetches data from [URL] and shows it on the page." It will generate the whole pattern for you. --- # Your First `fetch()` `fetch()` is built into every browser. Three steps: ```js // 1. Fetch the URL const response = await fetch('https://oim.zhili.dev/words/random'); // 2. Parse the JSON body const word = await response.json(); // 3. Use the data console.log(word); ``` **fetch** the URL, convert to **JSON**, **use** the data. --- # The Response Object `fetch()` returns a Response with useful properties: ```js const response = await fetch('https://oim.zhili.dev/words/random'); console.log(response.status); // 200 console.log(response.ok); // true (status 200-299) ``` | Property | Meaning | |---|---| | `response.status` | HTTP status code (200, 404, 500) | | `response.ok` | `true` if status is 200-299 | | `response.json()` | Parse body as JSON (returns a Promise) | --- # Working with Rich API Data ```js async function showTowns() { const response = await fetch('https://oim.zhili.dev/mass'); const data = await response.json(); console.log(data.name); // "Massachusetts" console.log(data.governor); // "Maura Healey" for (const town of data.data) { console.log(`${town.name}: pop ${town.population}`); } } ``` Use dot notation to reach into nested JSON. --- layout: center --- # Error Handling with try/catch --- # try/catch Pattern API calls can fail (network down, bad URL, server error). Wrap `fetch` in `try/catch`: ```js async function getRandomWord() { try { const response = await fetch('https://oim.zhili.dev/words/random'); const word = await response.json(); document.querySelector('#result').textContent = word; } catch (error) { document.querySelector('#result').textContent = 'Failed to load'; console.error('Error:', error); } } ``` - **`try`** -- run this code - **`catch`** -- if anything throws an error, run this instead > AI tip: If your fetch code works sometimes but fails other times, ask AI: "Add try/catch error handling to this fetch call." --- layout: center --- # Live Demo: OIM Teaching API --- # Try It: Random Word Open your browser console (`F12`) and paste: ```js const response = await fetch('https://oim.zhili.dev/words/random'); const word = await response.json(); console.log(word); ``` Run it a few times -- you get a different word each time! Note: `await` works at the top level in the console. In a `.js` file, always wrap it in an `async function`. API docs: [oim.zhili.dev/docs](https://oim.zhili.dev/docs) --- # Displaying API Data on the Page ```js async function showTowns() { const response = await fetch('https://oim.zhili.dev/mass'); const data = await response.json(); const list = document.querySelector('#town-list'); for (const town of data.data.slice(0, 10)) { const li = document.createElement('li'); li.textContent = `${town.name} (${town.county}) - pop ${town.population.toLocaleString()}`; list.appendChild(li); } } ``` Same **loop + DOM** pattern from last session, now with real data! --- # Pattern: Loading State Show the user something is happening: ```js async function loadData() { const result = document.querySelector('#result'); result.textContent = 'Loading...'; try { const response = await fetch('https://oim.zhili.dev/mass'); const data = await response.json(); result.textContent = `Found ${data.data.length} towns`; } catch (error) { result.textContent = 'Error loading data'; } } ``` > AI tip: Ask AI "add a loading spinner to my fetch function" and it will generate the loading state pattern for you. --- # Pattern: Search + Display ```js async function findTown() { const search = document.querySelector('#search').value.toLowerCase(); const response = await fetch('https://oim.zhili.dev/mass'); const data = await response.json(); const matches = []; for (const town of data.data) { if (town.name.toLowerCase().includes(search)) { matches.push(town); } } // Render the matches const list = document.querySelector('#results'); list.innerHTML = ''; for (const town of matches) { const li = document.createElement('li'); li.textContent = `${town.name} (${town.county})`; list.appendChild(li); } } ``` --- # GET vs POST ```js // GET: read/fetch data (default) await fetch('https://oim.zhili.dev/words/random'); // POST: send/submit data await fetch('https://oim.zhili.dev/message', { method: 'POST', body: JSON.stringify({ message: 'Hello from Alice!' }) }); ``` - **GET** = "give me data" (reading) - **POST** = "here's some data" (writing) > AI tip: When you ask AI to connect to an API, tell it whether you need GET (reading data) or POST (sending data). This avoids confusion. --- # Your Turn: Word Display Create `js/word-fetch.js` linked from an HTML page with a button and a `<p id="result">`. 1. Write an `async function` that fetches a random word from `https://oim.zhili.dev/words/random` 2. Display the word in the `#result` paragraph 3. Add `try/catch` so the page shows "Failed to load" if the request fails 4. Wire the button's click event to call your function > AI tip: Paste your HTML into AI and say "write a fetch function that loads a random word and displays it in #result." Compare its code to yours. --- # Key Takeaways 1. APIs let your code **request data** from servers 2. `fetch()` makes HTTP requests; `await` waits for the response 3. Always parse the response with `.json()` before using it 4. Wrap every `fetch` in **`try/catch`** to handle errors 5. Use **loading states** so users know something is happening --- # References - [OIM Teaching API docs](https://oim.zhili.dev/docs) - [MDN: Using Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) - [MDN: async/await](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await)

Topics Covered

  • fetch
  • await
  • JSON
  • request/response shape
  • basic error handling with try/catch

Content Slides Open fullscreen ↗

Taught In

  • Monday, 6/22 — JS async + fetch · API keys + .gitignore