Exercise: Dog Breed Explorer
Finish an animal shelter's dog breed explorer, writing the functions that read the Dog CEO API's breed list, build a photo's address and load a photo with friendly errors, with offline tests for the functions and a checklist for the page's loading, empty and error states.
Journeyman · JavaScript · REST API · Web Development · Updated
Riverbend Animal Shelter has dogs of every shape and size, and the people who come to adopt often ask the same question: "What does a Shiba Inu actually look like?" The shelter's volunteers have built a Dog Breed Explorer page: a search box, a button for every breed, and a panel for a photo. It already loads the breed list live from the Dog CEO API. What's missing is the part that talks to the API about photos, and what the page says while it waits or when something goes wrong. You'll write it, and tests will check your functions without even touching the internet.
Before you start
You need VS Code with Live Preview, a browser and Node.js, which Setup: VS Code, a Browser and Node.js installs. The exercise uses fetch, response.ok and AbortSignal.timeout from this chapter, try/catch (Handling Errors with try, catch and finally), and import and export (Modules: import and export). Nothing from any other exercise is needed: the folder has the whole page.
Get the exercise code
In the Exercise box beside the lesson, choose Get the starter on GitHub to open its GitHub repository. Click Code, then Download ZIP, and unzip it into your course folder. Or clone it with Git.
The folder holds:
index.htmlandstyles.css: the explorer page and its look. Ready to use: you don't write any HTML or CSS.saved-breeds.js: a saved copy of the API's breed list. The page uses it when the live list can't load, and the tests use it so they run offline. Ready to use.dogs.js: three functions that work with the Dog CEO API, with no page involved. For you to finish.app.js: connects the page todogs.js. Loading the breed list and drawing the buttons are ready to use; two TODOs are for you to finish.dogs.test.js: the tests fordogs.js. You don't need to change this file.package.jsonandREADME.md: ready to use. The README has the same steps as this lesson, and the page checklist.
Open the folder in VS Code (File > Open Folder).
What to do
The comment at the top of dogs.js says what each function should do, with examples. Each one returns an empty value for now:
export function breedNames(apiData) {
return [];
}
export function imageUrlFor(breed) {
return '';
}
export async function loadRandomPhoto(breed, fetchFn = fetch) {
return '';
}
breedNames(apiData)takes the API's whole answer,{ message: { ... }, status: 'success' }, and gives back the breed names as an array in alphabetical order.imageUrlFor(breed)gives back the random-photo address for a breed, such ashttps://dog.ceo/api/breed/beagle/images/random, after tidying the name: no spaces at the ends, all lowercase.loadRandomPhoto(breed, fetchFn = fetch)asks for a photo with an 8-second timeout and gives back its address. When something goes wrong, it throws an error whose message a visitor understands: one for a404, one for any other failing status, one for a timeout, and one for no answer at all. The exact words are in the comment.- Finish
app.js. TODO 1 is the search's empty state (No breeds match "zebra". Try fewer letters.). TODO 2 is the photo panel: a loading message, then the photo, or the error's message and a Try again button. - Stretch goal: make the Surprise me button show a photo of a random breed.
What's fetchFn = fetch? The = fetch is a default parameter: when the page calls loadRandomPhoto('beagle') with one argument, fetchFn is the real fetch. The tests call it with a second argument, a fake fetch: a small function that answers the way the Dog CEO API would (a 200 with a photo, a 404, a timeout) without going online. A stand-in like that is called a test double, and it lets the tests try every failure on purpose, every time.
Check your work
Open VS Code's terminal (Terminal > New Terminal) and run the tests:
node --test
At first all 11 tests fail. That's expected: each function you finish turns its tests green. When a test fails, its message says what it expected, such as A 404 is still an answer, so fetch doesn't reject: check response.ok, then response.status.
Then open index.html with Live Preview in your browser (Live Preview: Show Preview (External Browser) from the Command Palette) and go through the checklist in README.md. It has you search for terr and zebra, fetch a beagle, switch the Network tab to Offline to see your error state and Try again, and break the breed list's address on purpose to see the saved copy take over. The page has to be opened with Live Preview: opened straight from its file, it can't load its modules.
Hints
- The names are the keys. The breed list's
messageis an object with one key per breed, and Getting Data with fetch shows a way to get an object's keys as an array. Then put them in order. - Keep the try small. In
loadRandomPhoto, puttry/catcharound thefetchFncall only. If thethrowfor a bad status sits inside the sametry, your own error lands in your owncatch, and a404gets reported as "couldn't reach". - Status first, body second. Check
response.okbeforeresponse.json(). When it'sfalse,response.statustells a404from the rest (When fetch Fails). - The error's name tells them apart. A timeout rejects with an error named
TimeoutError; anything else that rejects means there was no answer. - The page's states. In
showPhoto, set the loading message and hide the photo and Try again before theawait, as in Loading, Empty and Error States. Thentryfor the photo andcatchfor the message.
When you're done
Every test is green, and the summary at the end of the run says:
ℹ tests 11
ℹ suites 0
ℹ pass 11
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
On the page, clicking beagle shows Fetching your beagle photo..., then a beagle, and the panel says:
Here's your beagle! Pick it again for another photo.
With the Network tab set to Offline, clicking pug says We couldn't reach the dog photo service. Check your internet connection. and shows Try again.
Now look back at your loadRandomPhoto. The tests tried a 404, a 500, a timeout and no internet, in a fraction of a second, without a network. How long would it take to check all four by hand, on a real connection, every time you changed the function?
So what
Live data, a timeout, a message for every failure and a fake for the tests: that's how professional apps call APIs, from a weather widget to a banking app, and now the shelter's visitors can meet any breed in a click.