Understanding the JavaScript Fetch API with Examples
JavaScript can send network requests to the server and load new information whenever it’s needed.
For example, we can use a network request to:
-
Submit an order,
-
Load user information,
-
Receive latest updates from the server,
-
…etc.
…And all of that without reloading the page!
There are multiple ways to send a network request and get information from the server.
The fetch API is modern, versatile and natively available, so I’ll discuss and implement it in detail in this article. I'll also shed light on some of the cool features it has to offer.
It’s not supported by old browsers (can be polyfilled), but very well supported among the modern ones.
Let's get started!
How does Fetch API work?
For basic HTTP requests, using fetch()
is a three-step process:
-
Call
fetch()
, passing the URL whose content you want to retrieve. -
Get the
Response
object that is asynchronously returned by step 1 when HTTP response begins to arrive and call a method of thisResponse
object to ask for the body of the response (data). -
Get the body object that is asynchronously returned by step 2 and process it however you want.
The fetch()
API is completely Promise-based, and there are two asynchronous steps here, so you typically expect two then()
calls or two await
expressions when using fetch()
. (And if you’ve forgotten what those are, you may want to reread this article.)
Here’s what a fetch()
request looks like if you are using then()
and expect the server’s response to your request to be JSON-formatted:
fetch("/api/users/current") // Make a GET request .then((response) => response.json()) // Parse its body as a JSON object .then((currentUser) => { // Then process that parsed object displayUserInfo(currentUser); });
Here’s a similar request made using the async
and await
keywords to an API that returns a plain string rather than a JSON object:
async function isServiceReady() { let response = await fetch("/api/service/status"); let body = await response.text(); return body === "ready"; }
If you understand these two code examples, then you know 80% of what you need to know to use the fetch()
API. The next few sections that follow will demonstrate how to make requests and receive responses that are somewhat more complicated than those shown above.
HTTP Status Codes, Response Headers and Network Errors
The three-step fetch()
process shown in the previous section omits all error-handling code. Here’s a more realistic version:
fetch("/api/users/current") .then((response) => { // Check if the response is okay and has the expected type. if ( response.ok && response.headers.get("Content-Type") === "application/json" ) { // If successful, return a Promise for the parsed JSON body. return response.json(); } else { // If not successful, throw an error. throw new Error( `Unexpected response status ${response.status} or content type` ); } }) .then((currentUser) => { // When the Promise for the JSON body resolves, do something with the parsed body. displayUserInfo(currentUser); }) .catch((error) => { // If anything goes wrong, log the error. // If the user's browser is offline, fetch() itself will reject. console.log("Error while fetching current user:", error); });
Let's break down the code point by point:
-
Promise Resolution by
fetch()
:- The
fetch()
function returns a Promise that resolves to aResponse
object. - This
Response
object represents the response to the request made byfetch()
.
- The
-
Partial Resolution of Promise:
fetch()
resolves its Promise as soon as the server's response headers and HTTP status are available.- At this point, the full response body may not have arrived yet.
-
HTTP Status Code Check:
- The
status
property of theResponse
object contains the HTTP status code. - The
ok
property istrue
if the status is between 200 and 299, indicating a successful request. - The
ok
property isfalse
for any other status code.
- The
-
Examination of Headers:
- The
headers
property of theResponse
object is aHeaders
object. - You can use the
has()
method to test for the presence of a header. - Use the
get()
method to retrieve the value of a header. - HTTP header names are case-insensitive.
- The
-
Parsing JSON Body:
- The
response.json()
method is used to parse the response body as JSON. - This method returns a Promise that resolves to the JSON representation of the response body.
- It reads the body of the response stream and parses it as JSON, transforming the raw data into a JavaScript object.
- If the response body is not valid JSON, a SyntaxError is thrown, and the Promise is rejected.
- The
-
Chaining the Next .then():
- The
response.json()
method is typically used within the first.then()
block of the Promise chain after the initial fetch. - Once the JSON parsing is successful, the next
.then()
block is executed, and it receives the parsed JSON as its argument. - In the provided code, the parsed JSON representing the current user is passed to the displayUserInfo function within the second
.then()
block.
- The
-
Handling Promise Rejection:
- Regardless of the server's response (e.g., 404 Not Found or 500 Internal Server Error),
fetch()
fulfils its Promise with a Response object. fetch()
rejects its Promise only if it cannot contact the web server.- Rejection can occur if the user's computer is offline, the server is unresponsive, or the specified URL has a non-existent hostname.
- Regardless of the server's response (e.g., 404 Not Found or 500 Internal Server Error),
-
.catch() for Error Handling:
- It is recommended to include a
.catch()
clause after afetch()
call to handle errors. - This ensures proper error handling for scenarios where the network request fails, and the Promise is rejected.
- It is recommended to include a
Specifying request header and request method
In each of the fetch()
examples shown so far, we have made an HTTP (or HTTPS) GET request. If you want to use a different request method (such as POST, PUT, or DELETE), simply use the two-argument version of fetch()
, passing an Options
object with a method parameter:
fetch(url, { method: "POST", }) .then((response) => response.json()) .then(handleResponse);
POST and PUT requests typically have a request body containing data to be sent to the server. As long as the method property is not set to 'GET' or 'HEAD' (which do not support request bodies), you can specify a request body by setting the body property of the Options object:
fetch(url, { method: "POST", body: "hello world", });
When you specify a request body, the browser automatically adds an appropriate 'Content-Length' header to the request.
When the body is a string, as in the preceding example, the browser defaults the 'Content-Type' header to 'text/plain;charset=UTF-8.' You may need to override this default if you specify a string body of some more specific type such as 'text/html' or 'application/json':
fetch(url, { method: "POST", headers: new Headers({ "Content-Type": "application/json" }), body: JSON.stringify(requestBody), });
Summary
A typical fetch request consists of two await
calls:
let response = await fetch(url, options); // resolves with response headers let result = await response.json(); // read body as json
Or, without await
:
fetch(url, options) .then(response => response.json()) .then(result => /* process result */)
Response properties:
response.status
– HTTP code of the response,response.ok
–true
if the status is 200-299.response.headers
– Map-like object with HTTP headers.
Methods to get response body:
response.text()
– return the response as text,response.json()
– parse the response as JSON object,
Fetch options so far:
method
– HTTP-method,headers
– an object with request headers (not any header is allowed),body
– the data to send (request body)
Get my free, weekly JavaScript tutorials
Want to improve your JavaScript fluency?
Every week, I send a new full-length JavaScript article to thousands of developers. Learn about asynchronous programming, closures, and best practices — as well as general tips for software engineers.
Join today, and level up your JavaScript every Sunday!
Thank you, Taha, for your amazing newsletter. I’m really benefiting from the valuable insights and tips you share.