Communicating with Servers

Many web pages are served out full of holes. Those holes are filled in asynchronously, after the initial render, with content that's pulled from a database. This ability to save and load data and modify the content of a page dynamically has turned web development into full-fledged application development.

In 1999, web developers began using JavaScript to issue requests to servers, which queried the database and returned the results in an XML format. This technique was called AJAX (Asynchronous JavaScript and XML). These days you are more likely to find servers returning the data as JSON.

The fetch function is used to issue requests to servers. The server's URL is passed as a parameter. The URL can point to an image or an HTML document, but it often points to a web service. The web service receives the URL and interprets it like a function call. For example, consider the free PokéAPI, a web service that provides access to database of information about the Pokémon universe. This service provides many endpoints, which are the names of the functions the service can execute. Each endpoint defines what parameters its expects.

Try inspecting the pokemon-species endpoint. Its URL has the form https://pokeapi.co/api/v2/pokemon-species/ID-OR-NAME. Try visiting https://pokeapi.co/api/v2/pokemon-species/weedle in your browser. In some browsers, you'll see a page full of JSON-formatted data. Try visiting the page in Firefox, and you will see it in a more readable form.

Try removing the species parameter from the URL. You should see an array of species. Suppose you wanted to turn that array into an unordered list. In a script, you grab a reference to your ul and issue a fetch call:

html
js
preview

When you open this example as popup and inspect your browser's developer tools, you find that the value returned by fetch is not actually the data from the server. Rather, it's a Promise object. The fetch function must communicate with a remote machine over the network, which may be slow. To prevent the local browsing experience from being slowed down by this network request, fetch is run asynchronously. The calling script picks up executing immediately.

Eventually, the data from the server will arrive, and you want to process it. But where do you put that code? You don't know, because you don't know when fetch will finish.

You must turn to the promise, which serves as an event mediator. When fetch finishes, it will fulfill the promise it made to grab some data from a URL. You attach your processing code to that promise, and the code will be run when the promise is fulfilled. You attach your code using the then function, which behaves a bit like addEventListener:

html
js
preview

When you open this example as a popup and examine the log, you see that the response isn't quite in a usable form yet. What you have in your hands is an HTTP response object. What you want is the data locked up inside the response's body. The response object has a method for expanding its JSON-formatted body into JavaScript data. However, it too is an asynchronous function. You must use a couple of then functions to chain the promises together in sequence:

html
js
preview

When you open this example as a popup and examine the log, you see your beautiful data. From there, you modify the process function to iterate through the results and turn each species into a list item:

html
js
preview

Note that the PokéAPI web service doesn't give you all 900+ species at once. The offset and limit parameters are used to page through the results.

Try querying a different endpoint and displaying the results in an appropriate way.