Nowadays it is difficult to imagine at least a slightly serious web application without the interaction of the client (browser) and the server because the vast majority of data is stored in databases. The users will probably need to send a message to another user, download a list of products from a database, publish a blog post, and so on.
The Fetch API is an interface through which the client can interact with the server: send HTTP requests to the server and receive a response from it. The peculiarity of the Fetch API is that it allows sending asynchronous requests. This means that the rest of the JavaScript code will continue to execute while we wait for data from the server.
Fetch method
Some ten years ago, web developers used XMLHttpRequest, an API that allowed the client and server to exchange data, but it was not perfect. It was replaced by the Fetch API with similar functionality. With it, the code became much simpler and cleaner, since it uses an object of the Promise class. Therefore, we can use the convenient asynchronous request processing syntax provided by the Promise class.
To use the Fetch API, all modern browsers have a fetch() method that you can call. With it, you can both send and receive data.
The algorithm of actions when using fetch() is quite simple. So, for example, to send a GET request (the base request method), you need to:
1) Call fetch(), passing the URL of the content we need to get.
2) Call the response method. Usually, this is the .json() method since the response usually comes in JSON format (sometimes this action is called unpacking). The .json method also returns an object of the Promise class, this should be taken into account when processing data further.
3) Process the response body with the class methods Promise .then and .catch or "async await" syntax. They have the same functionality, so you can choose any method.
The first code example is the same as the second code example, only processed with different syntax.
// first example code
const test = fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.log(error));
// second example code
async function fetchFunction() {
try{
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const data = await response.json();
console.log(data);
} catch(error){
console.log(error)
}
}
As a result, we will print to the console the data that the server will give us or print an error to the console if it fails.
Sending a request
With the fetch() method, we can send HTTP requests such as GET, PUT, POST, PATCH, etc. We will look at the full variety of HTTP requests a little later, but for now, let's focus on the basic GET method. When calling the fetch() method, we must pass the URL address as the first argument. It takes an options object as the second argument. The most commonly used options are: method, which indicates the request method, headers, i.e. HTTP headers, body, the request body for POST and PUT request methods, and others.
Query params
For example, to filter, sort data or get some part of the data that is located at the specified URL, you need to set the request parameters along with the URL. To do this, after the URL write the symbol ? and insert name=value pairs. Separate the query params with the & symbol.
fetch("https://jsonplaceholder.typicode.com/users?_limit=10&page=3");
Additionally, we can use the built-in URL object to create a URL and include parameters in it.
const url = new URL("https://jsonplaceholder.typicode.com/users");
url.searchParams.set("_limit", "10");
url.searchParams.set("page", "3");
fetch(url);
The names of the query params and how they need to work are sometimes different, as this is determined by the server developers.
Request headers
Sometimes it is necessary to set headers in requests, for example, if we need to make a request that requires any credentials. Usually, this is a unique authorization token, something like a temporary data access key. In this case, we must pass an object with the headers property as the second argument to the fetch() method. To do this, we can use the built-in Headers object.
const authHeaders = new Headers();
authHeaders.set("Authorization", "ea135929105c4f29a0f5117d2960926f");
fetch("https://jsonplaceholder.typicode.com/users" , {
headers: authHeaders
});
You can also add request headers to options as an object.
fetch("https://jsonplaceholder.typicode.com/users" , {
headers: {
"Authorization": "ea135929105c4f29a0f5117d2960926f"
}
});
Methods and request body
In order to apply a request method other than the default GET method, you need the options argument of the fetch() method to add the method property.
Requests, such as POST and PUT, usually have a request body containing the data we want to send to the server. For example, such methods are commonly used to add or change data in a database or login and password authentication. Usually, we pass a string as data in the request body, since this is how the data is stored in the database. Thus, you can convert an object to a string using the JSON.stringify() method.
const requestBody = {
name: "John",
age: "16"
};
fetch("https://jsonplaceholder.typicode.com/users" , {
method: "POST",
body: JSON.stringify(requestBody)
});Processing response
The fetch() method returns a Promise object which will have properties such as:
1. status — response code;
2. statusText — a message describing the response code;
3. ok — a boolean value that indicates the success of the request;
4. headers - list of response headers;
5. url — request URL;
6. body — data sent by the server, which we cannot read yet since it is in the ReadableStream format;
7. bodyUsed — boolean value indicating data is being read;
and other less important properties.
The data that came in the response body in ReadableStream format cannot be used immediately. First, it needs to be converted to a suitable format using methods such as text(), json(), etc. The json() method is usually used for this. After that, we can use the data as we need.
Processing errors
There are two ways of processing errors. If you are using the classic .then/.catch way of processing a request, you should place the code that will execute in case of an error in the .catch block.
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((json) => console.log(json))
.catch((e) => console.log(e.message));
If you use the async/await syntax, then you need to wrap the request in a try/catch construct, and place the code that will be executed in case of a request error in the catch block.
async function fetchFunction() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const data = await response.json();
console.log(data);
} catch (e) {
console.log(e.message);
}
}
At first glance, handling request errors seems simple, but there is one detail to consider: our request will only throw an error if the request did not happen due to a network failure or something else prevented its execution.
Imagine a situation where we are only satisfied with the successful execution of a request with a status of 200—209, since we want to receive data. Therefore, we can raise an error ourselves if the response code does not match the expected one and this will transfer the code execution to the .catch method.
// example with .then/.catch
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => {
if (response.status >= 200 && response.status <= 209) {
const data = response.json();
return data;
} else {
throw new Error("Incorrect server response");
}
})
.then((json) => console.log(json))
.catch((error) => console.log(error.message));
// example with try/catch
async function fetchFunction() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (response.status >= 200 && response.status <= 209) {
const data = await response.json();
console.log(data);
} else {
throw new Error("Incorrect server response");
}
} catch (error) {
console.log(error);
}
}Conclusion
The Fetch API serves web developers to communicate between client and server. Using the .fetch method, you can access the server from the browser, receive data from it, and perform operations of adding, changing, and deleting data.
To send a request, you must specify the URL address where the server is located. In addition, the fetch() method takes an object with detailed request settings as the second parameter. There you can specify query params, request headers, methods, and request body. Since the fetch() method is based on the concept of the Promise class, you can conveniently handle the server response using the Promise object's .then/.catch and async/await syntax methods.