API stands for Application Programming Interface, it is a technology for websites and services to communicate together, sending and retrieving data, without reloading the page.
1. Fetch
fetchis a Web API, which allows to request data to a server – ours or an external one. The important part is it allows to retrieve data without reloading the page.
fetch(URL) is the first step, we need a url to which we request data.
This request will come back with a response. The response can be text, JSON, binary data, or any other formats. Most of the time, it’s JSON.
fetch(URL)// ... then response
This response is a promise.
Base URL & endpoints
API URLs are generally composed two parts: a base URL, and endpoints to different types of data.
Example for a service API with different endpoints:
Here, the base URL is https://service.com/api/v1, and endpoints are
users.json
notifications.json
messages.json
account/password.json
fetch("https://service.com/api/v1/users.json")// ... then response
This logic of Base URL and endpoints avoids repetition and makes documentation of an API easier.
When working with APIs, the terms URLs and endpoints can be used interchangeably, but throughout any API documentation, endpoint is most commonly used.
2. Response Promise
After fetching, response is an intermediary step before actually accessing and using the fetched data.
Promises in JavaScript allows to schedule work in the future, and run callbacks based on the outcome of the promise (if it succeeded or not).
Since fetch returns the Response object as a promise, we have to consider what to do depending if the promise succeeds or not.
We resolve the promise using a then(callback), running once the fetch request has completed. The first argument is called by convention response.
Fetch is a generic browser function, and it doesn’t know what type of data is received, for this reason we generally manually convert the response into an object:
[ { "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "Sincere@april.biz", ... // other key/values here }, { "id": 2, "name": "Ervin Howell", "username": "Antonette", "email": "Shanna@melissa.tv", ... // other key/values here }, { ...}]
This structure is an array of objects.
If we would want to log emails of every user:
fetch("https://jsonplaceholder.typicode.com/users") .then(response => response.json()) .then(data => { console.log(data); // visualize it then realize it's an array of objects data.forEach(user => { // console.log(user); // // visualize how each item of the array look like console.log(user.email); // log the email }); });
Handling fetch errors
Response status codes
Before learning how to handle fetch errors, let’s see what status codes can a response provide.
When you perform a fetch request to an API, it will respond with a status code that could be in one of the following ranges:
The fetch() promise rejects for network errors, not client or server errors (4xx, 5xx).
This means fetching a URL that does not exist (404) will have its promise fulfilled. The then(callback) with be executed, catch(callback) will only execute in case of network failure.
A better error handling would then cover to check in the response object for a status code (providing it’s available in the API we work with):
fetch(URL) .then(response => { if (!response.ok) { // 4xx or 5xx error throw new Error("API issues."); } return response.json(); }) .then(data => { console.log(data); }) .catch(error => { console.error(error); });
When response.ok is false, in that case, we throw a new error. The throw new Error("API issues.") will reject the promise, so, the code stops running the current .then(callback) and then jumps to the .catch(callback).
TLDR fetch boilerplate
fetch(URL) .then(response => { // console.log(response;) // check if response object has status code if (!response.ok) { // 4xx or 5xx error throw new Error("API issues."); } return response.json() }) .then(data => { console.log(data); }) .catch(error => { console.error(error); })