JavaScript Fetch API
Introduction
The Fetch API is a modern interface for making HTTP requests to servers from web browsers. It provides a more powerful and flexible feature set compared to older techniques like XMLHttpRequest. The Fetch API uses Promises, which enables a simpler and cleaner API, avoiding callback hell and making it easier to write asynchronous code.
In this tutorial, we'll explore how to use the Fetch API to request data from servers, handle responses, and manage different scenarios that arise during network communications.
Fetch API Basics
The fetch()
function is the primary interface for making requests with the Fetch API. It returns a Promise that resolves to the Response object representing the response to the request.
Simple Fetch Request
Here's the most basic syntax:
fetch(url)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Let's break down what's happening:
- The
fetch()
function takes a URL as input - It returns a Promise that resolves to a Response object
- We use
.then()
to process the Response, converting it to JSON - Another
.then()
processes the actual data - The
.catch()
handles any errors that might occur
Making Different Types of Requests
GET Request (Default)
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
POST Request
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John Doe',
email: '[email protected]'
})
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
Other Request Methods
Fetch supports all standard HTTP methods:
// PUT request
fetch('https://api.example.com/data/1', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Updated Name',
email: '[email protected]'
})
})
.then(response => response.json())
.then(data => console.log('Updated:', data));
// DELETE request
fetch('https://api.example.com/data/1', {
method: 'DELETE',
})
.then(response => response.json())
.then(data => console.log('Deleted:', data));
Handling Responses
The Response object provides several methods to handle the response data in different formats:
JSON Response
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
Text Response
fetch('https://api.example.com/text')
.then(response => response.text())
.then(text => console.log(text));
Binary Data (Blob)
fetch('https://api.example.com/image.jpg')
.then(response => response.blob())
.then(blob => {
const imageUrl = URL.createObjectURL(blob);
const imgElement = document.createElement('img');
imgElement.src = imageUrl;
document.body.appendChild(imgElement);
});
Response Properties and Methods
The Response object has several useful properties:
fetch('https://api.example.com/data')
.then(response => {
// Response properties
console.log(response.status); // 200, 404, etc.
console.log(response.statusText); // OK, Not Found, etc.
console.log(response.ok); // true if status is 200-299
console.log(response.headers); // Headers object
return response.json();
})
.then(data => console.log(data));
Error Handling
Error handling is crucial when working with network requests. Here's how to handle different scenarios:
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
// This will catch HTTP errors like 404, 500, etc.
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
// Process your data here
console.log('Success:', data);
})
.catch(error => {
// This will catch network errors or errors thrown manually
console.error('Error:', error.message);
});
Using Fetch with Async/Await
The Fetch API becomes even cleaner when used with async/await
:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
console.error('Fetch error:', error);
}
}
// Call the function
fetchData();
Real-World Example: Fetching Weather Data
Let's create a practical example showing how to fetch weather data from a public API:
async function getWeather(city) {
const apiKey = 'your_api_key'; // Replace with your API key
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Weather API error! Status: ${response.status}`);
}
const data = await response.json();
// Create weather information
const weather = {
city: data.name,
country: data.sys.country,
temperature: data.main.temp,
description: data.weather[0].description,
humidity: data.main.humidity,
windSpeed: data.wind.speed
};
displayWeather(weather);
return weather;
} catch (error) {
console.error('Error fetching weather:', error);
displayError(error.message);
}
}
function displayWeather(weather) {
const weatherDiv = document.getElementById('weather-info');
weatherDiv.innerHTML = `
<h2>${weather.city}, ${weather.country}</h2>
<p>Temperature: ${weather.temperature}°C</p>
<p>Description: ${weather.description}</p>
<p>Humidity: ${weather.humidity}%</p>
<p>Wind Speed: ${weather.windSpeed} m/s</p>
`;
}
function displayError(message) {
const weatherDiv = document.getElementById('weather-info');
weatherDiv.innerHTML = `<p class="error">Error: ${message}</p>`;
}
// Event listener for the form submission
document.getElementById('weather-form').addEventListener('submit', function(e) {
e.preventDefault();
const city = document.getElementById('city-input').value;
getWeather(city);
});
In this example, you would also need HTML like:
<form id="weather-form">
<input type="text" id="city-input" placeholder="Enter city name" required>
<button type="submit">Get Weather</button>
</form>
<div id="weather-info"></div>
Advanced Fetch Techniques
Setting Request Timeout
The Fetch API doesn't have a built-in timeout option, but you can create one with Promise.race()
:
async function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const { signal } = controller;
// Create a timeout promise
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
controller.abort();
reject(new Error('Request timeout'));
}, timeout);
});
try {
// Race the fetch request against the timeout
const response = await Promise.race([
fetch(url, { ...options, signal }),
timeoutPromise
]);
return response;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request was aborted due to timeout');
}
throw error;
}
}
// Usage
fetchWithTimeout('https://api.example.com/data', {}, 3000)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
Uploading Files
You can use the Fetch API to upload files:
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`Upload failed with status: ${response.status}`);
}
const result = await response.json();
console.log('Upload successful:', result);
return result;
} catch (error) {
console.error('Error uploading file:', error);
throw error;
}
}
// Usage with a file input
document.getElementById('file-input').addEventListener('change', async (e) => {
if (e.target.files.length > 0) {
try {
const result = await uploadFile(e.target.files[0]);
alert('File uploaded successfully!');
} catch (error) {
alert(`Upload failed: ${error.message}`);
}
}
});
Browser Compatibility and Polyfills
The Fetch API is supported in all modern browsers, but for older browsers, you might need a polyfill. The most common one is whatwg-fetch. You can include it in your project via npm:
npm install whatwg-fetch
And then import it at the top of your JavaScript file:
import 'whatwg-fetch';
Summary
The Fetch API is a powerful and flexible way to make HTTP requests in JavaScript. In this tutorial, we've covered:
- Basic fetch request syntax with Promises
- Different HTTP methods (GET, POST, PUT, DELETE)
- Handling different types of responses (JSON, text, binary)
- Error handling strategies
- Using fetch with async/await
- Advanced techniques like request timeouts and file uploads
The Fetch API has become the standard way to make HTTP requests in modern web applications, replacing the older XMLHttpRequest. Its Promise-based nature makes it easier to write clean, maintainable asynchronous code.
Further Exercises
- Create a simple app that fetches data from a public API (like JSONPlaceholder) and displays it in a list
- Build a form that posts data to a server and handles the response
- Create a pagination system using fetch to load more data when a user scrolls or clicks a button
- Implement a retry mechanism that attempts to fetch data again if the initial request fails
- Build a client for a REST API that handles CRUD operations using different fetch requests
Additional Resources
- MDN Fetch API Documentation
- JavaScript.info Fetch Guide
- Google Developers Fetch Guide
- Public APIs List for practice
- Axios - A popular alternative to fetch with additional features
Happy coding!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)