JavaScript has revolutionized web development with its ability to handle asynchronous operations seamlessly. Among its many tools, the Fetch API stands out as a powerful interface for making HTTP requests. This blog explores how to master the Fetch API, covering its functionality, error handling, and practical use cases.
The Fetch API provides a simple and elegant way to make network requests. The basic syntax is as follows:
1fetch(
2 // url
3 'https://jsonplaceholder.typicode.com/posts/1',
4 // options
5 {
6 method: 'GET',
7 headers: {
8 'Content-Type': 'application/json',
9 },
10 params: {
11 title: 'foo',
12 body: 'bar',
13 },
14 }
15)
16 .then(response => response.json())
17 .then(data => console.log(data))
18 .catch(error => console.error('Error fetching data:', error));
url
, The URL to which the request is sent.options.method
, The HTTP method to use for the request.options.headers
, Specifies the headers to include in the request. This is useful for setting content types or authentication tokens.options.params
, This example makes a GET
request to https://api.example.com/data?title=foo&body=bar
. options.body
, The body of the request, typically used for POST
or PUT
requests (not relevant for GET).response.json()
.1const fetchData = async () => {
2 // more readable, avoids callback hell
3 const response = await fetch(
4 'https://jsonplaceholder.typicode.com/posts/1',
5 {
6 method: 'GET',
7 headers: {
8 'Content-Type': 'application/json',
9 },
10 params: {
11 title: 'foo',
12 body: 'bar',
13 },
14 }
15 );
16 const data = await response.json();
17 return data;
18};
When using the Fetch API, it is essential to handle errors gracefully. The catch
block is used to catch any errors that occur during the request. Additionally, you can check the response status to determine if the request was successful.
1const fetchData = async () => {
2 try {
3 const response = await fetch(
4 'https://jsonplaceholder.typicode.com/posts/1',
5 {
6 method: 'GET',
7 headers: {
8 'Content-Type': 'application/json',
9 },
10 params: {
11 title: 'foo',
12 body: 'bar',
13 },
14 }
15 );
16 // Check if the response status is OK (200)
17 if (response.status !== 200 || response.statusText !== 'OK') {
18 throw new Error(`Error: ${response.status} ${response.statusText}`);
19 }
20 const data = await response.json();
21 // check if data is valid
22 if (!data) {
23 throw new Error('Error: No data found');
24 }
25 // check if data is in expected format
26 if (typeof data !== 'object') {
27 throw new Error('Error: Invalid data format');
28 }
29 return data;
30 } catch (error) {
31 console.error('Fetch error:', error);
32 return null;
33 }
34};
35
36fetchData();
The Fetch API supports streaming large responses, allowing you to process data as it arrives. This is particularly useful for handling large files or real-time data streams.
1const fetchAndStitchJSON = async () => {
2 const response = await fetch(url);
3 const reader = response.body.getReader();
4 const decoder = new TextDecoder("utf-8");
5
6 let buffer = "";
7 let done = false;
8
9 while (!done) {
10 const { value, done: streamDone } = await reader.read();
11 done = streamDone;
12
13 if (value) {
14 console.log("Received chunk:", value);
15 // Decode and append the chunk to the buffer
16 buffer += decoder.decode(value, { stream: true });
17 }
18 }
19
20 try {
21 // Parse the complete JSON from the buffer
22 const jsonData = JSON.parse(buffer);
23 console.log("Parsed JSON:", jsonData);
24 } catch (error) {
25 console.error("Failed to parse JSON:", error);
26 }
27}
28
29fetchAndStitchJSON("https://jsonplaceholder.typicode.com/posts");
This receives the response in chunks and processes each chunk as it arrives.
1Received chunk: Uint8Array(3762) [
2 91, 10, 32, 32, 123, 10, 32, 32, 32, 32, 34, 117,
3 115, 101, 114, 73, 100, 34, 58, 32, 49, 44, 10, 32,
4 32, 32, 32, 34, 105, 100, 34, 58, 32, 49, 44, 10,
5 32, 32, 32, 32, 34, 116, 105, 116, 108, 101, 34, 58,
6 32, 34, 115, 117, 110, 116, 32, 97, 117, 116, 32, 102,
7 97, 99, 101, 114, 101, 32, 114, 101, 112, 101, 108, 108,
8 97, 116, 32, 112, 114, 111, 118, 105, 100, 101, 110, 116,
9 32, 111, 99, 99, 97, 101, 99, 97, 116, 105, 32, 101,
10 120, 99, 101, 112,
11 ... 3662 more items
12]
13
14Received chunk: Uint8Array(5445) [
15 32, 100, 111, 108, 111, 114, 101, 109, 113, 117, 101, 32,
16 110, 101, 113, 117, 101, 32, 102, 97, 99, 101, 114, 101,
17 92, 110, 97, 100, 32, 113, 117, 105, 32, 100, 111, 108,
18 111, 114, 117, 109, 32, 109, 111, 108, 101, 115, 116, 105,
19 97, 101, 32, 98, 101, 97, 116, 97, 101, 92, 110, 115,
20 101, 100, 32, 97, 117, 116, 32, 118, 111, 108, 117, 112,
21 116, 97, 115, 32, 116, 111, 116, 97, 109, 32, 115, 105,
22 116, 32, 105, 108, 108, 117, 109, 34, 10, 32, 32, 125,
23 44, 10, 32, 32,
24 ... 5345 more items
25]
26
27...
28
Tools like partial-json-parser can help with incrementally parsing JSON streams.
The Fetch API allows you to cancel in-flight requests using the AbortController
interface. This is useful for aborting requests that are no longer needed, such as when a user navigates away from a page or when a component unmounts.
1const controller = new AbortController();
2const { signal } = controller;
3
4const fetchData = async () => {
5 try {
6 const response = await fetch(
7 'https://jsonplaceholder.typicode.com/posts/1',
8 {
9 method: 'GET',
10 headers: {
11 'Content-Type': 'application/json',
12 },
13 params: {
14 title: 'foo',
15 body: 'bar',
16 },
17 signal, // Abort signal
18 }
19 );
20 if (response.status !== 200 || response.statusText !== 'OK') {
21 throw new Error(`Error: ${response.status} ${response.statusText}`);
22 }
23 const data = await response.json();
24 return data;
25 } catch (error) {
26 if (error.name === 'AbortError') {
27 console.log('Fetch aborted');
28 return null;
29 }
30 console.error('Fetch error:', error);
31 return null;
32 }
33};
34
35fetchData();
36controller.abort(); // Abort the fetch request
Behind the seenes, the AbortController
terminates the TCP
connection to the server, which means that the request will not be processed by the server. This is important for performance and resource management.
Diagram Loading...
Endpoints can be configured to handle halfOpen
connections. This means the server will continue trying to send data after acknowledging a FIN
packet. This is useful for long-lived connections, such as WebSockets or HTTP/2 streams.
However, this is not the default behavior for most HTTP servers. Most servers will close the connection immediately after sending a FIN_ACK
packet.
The Fetch API is a powerful tool for making HTTP requests in JavaScript. By mastering its features, including error handling, streaming, and cancellation, you can build robust and efficient web applications.