Using the Fetch API
Learn how to use the Fetch API to make HTTP requests in JavaScript, including GET and POST requests, handling responses, errors, and working with headers.
Introduction to the Fetch API
What is the Fetch API?
The Fetch API is a modern way to make network requests in JavaScript. It was introduced to provide a more powerful and flexible feature set compared to the traditional XMLHttpRequest
interface. The Fetch API is promise-based, making it easier to work with asynchronous operations, such as fetching data from a server or sending data to a server.
Imagine the Fetch API as a magic wand that allows you to ask for something and wait for it to be delivered. You wave the wand (make a fetch request) and then you wait for the response (the data you asked for). This waiting does not block other magical spells (other code) from being cast, making it perfect for modern web applications.
Why Use the Fetch API?
The Fetch API simplifies the process of making HTTP requests. Unlike XMLHttpRequest
, which is a bit clunky, Fetch provides a cleaner and more intuitive syntax. Additionally, Fetch is promise-based, which makes it easier to handle asynchronous operations. This means you can write code that feels synchronous and easier to read, thanks to async
and await
. Instead of callback hell or complex chaining of promises, Fetch provides a straightforward approach to dealing with network operations.
Setting Up Your Environment
Before we dive into using the Fetch API, let's make sure we have everything set up correctly.
Installing Node.js (if needed)
If you plan to use Node.js for some of the examples or to run a local server, you'll need to install Node.js. You can download it from the official Node.js website. The installation includes both Node.js and npm (Node Package Manager), which will be handy for managing dependencies in your projects.
Basic HTML Setup
For our examples, we'll need a simple HTML page with a script to run our JavaScript code. Here's a basic setup:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fetch API Examples</title>
</head>
<body>
<h1>Fetch API Examples</h1>
<script src="app.js"></script>
</body>
</html>
And create an app.js
file to write our JavaScript code:
// app.js
console.log('This is where our Fetch API examples will go.');
Basic Fetch Request
Let's start with the most common type of request: a GET request to fetch data from an API.
Making a Simple GET Request
Fetching Data from an API
Let's fetch some data from a public API. We'll use the JSONPlaceholder API, a free online REST API that you can use for testing and prototyping.
// app.js
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Explanation:
fetch(url)
: This initiates the network request. Theurl
parameter is the endpoint we are trying to reach..then(response => response.json())
: Thefetch
function returns a promise that resolves to theResponse
object representing the response to the request. We callresponse.json()
to parse the body of the response as JSON..then(data => console.log(data))
: The parsed JSON data is then logged to the console..catch(error => console.error('Error:', error))
: If something goes wrong, such as a network problem, the promise will reject, and we handle the error in thecatch
block.
Expected Output:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
This output represents the data fetched from the API, which is a single todo item in this case.
Viewing the Response in the Console
To see the response in the console, open your HTML file in a web browser and open the developer tools (usually by pressing F12 or right-clicking on the page and selecting "Inspect"). Navigate to the "Console" tab, and you should see the JSON data logged from the API.
Making a Simple POST Request
Sending Data to an API
Now, let's try sending data to an API using a POST request. We'll continue using the JSONPlaceholder API to create a new todo item.
// app.js
fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'Learn Fetch API',
completed: false,
userId: 1
})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Explanation:
fetch(url, options)
: The second parameter tofetch
is an options object where you can specify additional details about the request. Here, we set the method toPOST
, specify request headers, and provide a body for the request.headers
: We set theContent-Type
header toapplication/json
, indicating that the body of the request is in JSON format.body
: The body of the request contains the data we want to send, which is a JSON object here. We useJSON.stringify
to convert the JavaScript object to a JSON string..then(response => response.json())
and.then(data => console.log(data))
: These lines handle the response and log the data to the console..catch(error => console.error('Error:', error))
: This handles any errors that occur during the request.
Expected Output:
{
"title": "Learn Fetch API",
"completed": false,
"userId": 1,
"id": 201
}
The output includes the data we sent plus an id
that the server generates.
Understanding Responses
What is a Response Object?
The Response
object returned by the fetch
function contains various properties and methods that provide information about the response.
status
: The HTTP status code of the response.ok
: A boolean indicating whether the status code is in the range 200-299.headers
: A Headers object containing the response headers.text()
: A method to read the response body as plain text.json()
: A method to read the response body as JSON.
Checking the Status of a Response
You can check the status of a response using the status
property or the ok
boolean.
// app.js
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('There was a problem with the fetch operation:', error));
Explanation:
response.ok
: Ifresponse.ok
isfalse
, an error is thrown. ThestatusText
property provides a human-readable status message.
Expected Output:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Reading Response Text and JSON
The response body can be read as plain text or JSON. Since most APIs return data in JSON format, we usually use the response.json()
method.
// app.js
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Explanation:
response.text()
: This method reads the response body as plain text. For this example, it will log the JSON data as a string.
Expected Output:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Note: While response.text()
works, it's generally better to use response.json()
when you expect JSON data.
Handling Errors and Rejections
Understanding Promises
The Fetch API uses promises, which are a way to handle asynchronous operations in JavaScript. Promises represent a value that may be available now, in the future, or never. Promises are like a magical contract where you promise to do something, and you either fulfill that promise (resolve) or fail to do it (reject).
.catch()
Handling Promise Rejections with When a fetch operation fails (such as when the network is down or the server returns a bad status code), the promise is rejected. We can handle these rejections using the .catch()
method.
Handling HTTP Errors
HTTP errors (status codes like 404 or 500) are not considered exceptions. They are still considered successful fetch operations, so you must check the response.ok
property manually to handle HTTP errors.
response.ok
to Handle Errors
Using As mentioned earlier, you can use the response.ok
property to check if the response is successful. If not, you can throw an error.
// app.js
fetch('https://jsonplaceholder.typicode.com/todos/100000')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('There was a problem with the fetch operation:', error));
Explanation:
- Invalid URL: We're requesting a non-existent todo item (id 100000), so
response.ok
will befalse
. - Error Handling: Since the response is not ok, an error is thrown and caught in the
catch
block.
Expected Output:
There was a problem with the fetch operation: Error: Network response was not ok Not Found
Working with Headers
Headers are key-value pairs that provide additional information about the HTTP request or response. They can be used to specify things like content types, authentication tokens, and more.
Adding Headers to a Fetch Request
Sometimes, you need to add headers to your request, such as an authorization token or content type.
// app.js
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
},
body: JSON.stringify({
title: 'New Post',
body: 'This is the body of the new post',
userId: 1
})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Explanation:
headers
: We're adding two headers:Content-Type
to specify that the body of the request is JSON, andAuthorization
to provide an access token.Authorization
header: This is typically used for authentication. In this example,'YOUR_ACCESS_TOKEN'
should be replaced with a valid token.
Reading Headers from a Response
Sometimes, the response includes important headers that you need to read, such as the Content-Type
or Location
.
// app.js
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => {
console.log('Content-Type:', response.headers.get('Content-Type'));
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Explanation:
response.headers.get(headerName)
: This method retrieves the value of the given header from the response.
Expected Output:
Content-Type: application/json; charset=utf-8
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
// More posts...
]
Common Headers
Here are some common headers you might encounter:
Content-Type
: Specifies the media type of the resource. For JSON data, this is usuallyapplication/json
.Authorization
: Used for sending an access token as part of the request.Cache-Control
: Controls how the response is cached by the browser.
Using Async/Await for Cleaner Code
The Fetch API works with promises, but using async
and await
can make your code cleaner and easier to read. Let's see how.
Writing Asynchronous Code with Async/Await
Here's how you can rewrite our earlier GET request using async
and await
.
// app.js
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
}
fetchData();
Explanation:
async function
: Declares an asynchronous function.await
: Pauses the execution of the async function until the promise is resolved or rejected.try...catch
: This block is used to handle errors. If any part of the async function throws an error, the code inside thecatch
block is executed.
Expected Output:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Error Handling with Try/Catch
In the previous example, we used a try...catch
block to handle errors. This is the recommended way to handle errors when using async
and await
.
// app.js
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/100000');
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
}
fetchData();
Explanation:
if (!response.ok)
: Checks if the response is not successful.throw new Error
: Throws an error if the response is not successful.try...catch
: Catches any errors thrown in thetry
block.
Practical Example with Async/Await
Let's create a more practical example using async
and await
to fetch data and handle it.
// app.js
async function createPost() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'New Post',
body: 'This is the body of the new post',
userId: 1
})
});
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
}
createPost();
Explanation:
createPost
function: This function fetches data using a POST request.await response.json()
: Waits for the response to be parsed as JSON.- Error Handling: Checks if the response is not successful and throws an error if it isn't.
Expected Output:
{
"title": "New Post",
"body": "This is the body of the new post",
"userId": 1,
"id": 101
}
Fetch API and CORS
What is CORS and Why is it Important?
CORS (Cross-Origin Resource Sharing) is a security feature implemented by browsers to prevent web pages from making requests to a different domain than the one that served the web page. For example, if your web page is served from https://example.com
, it cannot make a fetch request to https://anotherdomain.com
unless that domain specifically allows it via CORS headers.
CORS is like a bouncer at a club. The club (origin) has rules about who can enter (which origins are allowed to make requests). If the rules are not met, the request is blocked.
Common CORS Errors and Solutions
Common CORS Errors
No 'Access-Control-Allow-Origin' header is present on the requested resource
: This means the server did not allow the request from your origin. The server needs to add theAccess-Control-Allow-Origin
header to its response.Method not allowed
: This error occurs when the server does not allow the HTTP method used in the request.
Solutions
- Server Configuration: Ensure the server is configured to accept requests from your origin by setting the
Access-Control-Allow-Origin
header. - Proxy Server: Use a proxy server to make requests on behalf of the client. This can help bypass CORS restrictions in development.
Exercises
Exercise 1: Fetching Data from a Public API
Try fetching data from the following public API endpoint:
https://jsonplaceholder.typicode.com/comments?postId=1
Log the response to the console.
Exercise 2: Sending Data to an API
Create a function that sends a new user to the following API endpoint:
https://jsonplaceholder.typicode.com/users
The request body should include the following data:
{
"name": "John Doe",
"email": "john.doe@example.com",
"username": "johndoe",
"address": {
"street": "123 Main St",
"suite": "Apt 4B",
"city": "Gotham",
"zipcode": "10001",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
}
}
Log the response to the console.
Summary and Next Steps
Recap of Key Points
- The Fetch API is a modern way to make HTTP requests in JavaScript.
- It is promise-based and simplifies asynchronous operations.
- You can handle responses using both
.then()
chains andasync/await
. - Errors in fetch operations can be handled using
.catch()
ortry...catch
blocks. - Headers can be added to requests and read from responses.
- CORS is an important consideration when making requests to different origins.
Suggested Further Reading
Next Steps in Learning APIs and Networking
- Learn about other HTTP methods like PUT, PATCH, and DELETE.
- Explore more about CORS and how to handle it.
- Experiment with different APIs and build small projects that involve fetching and displaying data.