So far, we should be familiar with the following concepts:
- HTTP requests and responses
- Asynchronous code is non-blocking
- We can use callbacks to execute code after an asynchronous operation completes. But this creates ugly "callback hell" code
- Promises are objects that represent the eventual completion of code. They start out pending and then are either fulfilled or rejected.
- A fulfilled Promise will invoke
resolve()
with the promised data - A rejected Promise will invoke
reject()
with error data .then()
can be used to handle fulfilled Promises.catch()
can be used to handle rejected Promises
Fetching is all about programmatically getting data from an API.
An API (Application Programming Interface) is a programming tool to perform a specific task. All APIs exposes an interface that let's programmer's use that tool. APIs come in many forms with different kinds of interfaces.
For example, the DOM API's interface comes in the form of methods attached to the document
object (e.g. document.getElementById
and document.createElement
). This is because the DOM API is focused on adding functionality to your application.
Web APIs focus on adding data to your applications and they use URLs to provide a direct path to their data.
To start, let's look at a Web API.
- Here are a few I recommend (From this list):
- Daily astronomical picture: https://go-apod.herokuapp.com/apod
- Get the current Sunrise/Sunset: https://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&date=today
- Paste the URL in the browser's address bar to see what it looks like when we fetch the data using the browser
You will most likely see something like this:
Every URL has a few parts. Understanding those parts can help us fetch precisely the data we want.
Consider this URL which tells us information about the sunrise and sunset at a partiuclar latitude and longitude:
https://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&date=2023-3-15
Let's break it down:
https://api.sunrise-sunset.org
— This is the host. It tells the client where the resource is hosted/located./json
- This is the path. It shows what resource is being requested?lat=36.7201600&lng=-4.4203400&date=2023-3-15
- These are query parameters and this particular URL has 3:lat
,lng
, anddate
. Query parameters begin with a?
are are separated with&
. Each parameter uses the formatname=value
. Try changing thedate
parameter!
When using a new API, make sure to look at that APIs documentation (often found at the host address) to understand how to format the request URL.
Look closely at the response data and you'll notice that the data is contained within curly braces {}
— it's an object! But it's a special kind of object called a JSON object.
JSON stands for JavaScript Object Notation. It is a file format for transmitting data via the internet.
Keep this in mind for later...
Click here to fork the example repo
fetch
is a method native to all browsers (and in Node v.18) that sends an HTTP request to the provided url
.
const fetchPromise = fetch('https://go-apod.herokuapp.com/apod');
// fetch returns a Promise object
console.log(fetchPromise); // prints [object Promise] { ... }
fetch
returns a Promise
that resolves to a response object containing the fetched data. Printing the returned Promise won't show us the data. We need to extract it.
To use a Promise's data, we use the then
method on the returned Promise
.
const dataPromise = fetchPromise.then(response => {
const data = response.json(); // converts response (which is in the JSON format) into a plain object
return data; // the value returned will be wrapped in a new Promise
})
Let's break it down:
then
takes a callback that is executed when thePromise
is fulfilled. The callback is given a single argument, the data that the Promise resolves to.- Promises returned by
fetch
resolve to JSON-formatted data, so we have to first convert it to a plain JavaScript object (response.json()
) then
also returns a Promise. That Promise will resolve to whatever its callback returns.
dataPromise.then(data => {
// do stuff with the data
console.log(data) // prints the raw data
})
Or, more elegantly:
fetch('https://go-apod.herokuapp.com/apod') // returns a Promise
.then(response => response.json()) // also returns a Promise
.then(data => {
// do stuff with the data
console.log(data) // prints the JSON data
})
To summarize:
fetch
is provided a url to fetch data from. It returns a Promise- When that
fetch
Promise is fulfilled, the firstthen
callback is executed. The resolvedresponse
from thefetch
call is provided as an argument. - The
response
from thefetch
call is in JSON format so we have to convert it to a plain JS object with the.json()
method. .json()
is also an asynchronous function that returns a Promise.- When the
response.json()
Promise is fulfilled, the secondthen
callback is executed. The resolveddata
fromresponse.json()
is provided as an argument and we can do whatever we need to with the data!
If the Promise is rejected, we need to handle the error that the Promise provides. To do this, we add the catch
method to our "Promise chain":
fetch('https://go-apod.herokuapp.com/apod') // returns a Promise
.then(response => response.json()) // also returns a Promise
.then(data => {
// do stuff with the data
console.log(data) // prints the JSON data
})
.catch(error => console.log(error))
Often, we need to manipulate the DOM in response to a fetch
call. To make a simple program that does this, we will typically follow these steps:
- Create HTML elements (give them ids)
- Create variables for those DOM elements in JS
- Fetch data
- convert from JSON
- use the data to manipulate the DOM elements
- Bonus: we can make a button to fetch new data