Skip to main content

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:

javascript
fetch(url)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

Let's break down what's happening:

  1. The fetch() function takes a URL as input
  2. It returns a Promise that resolves to a Response object
  3. We use .then() to process the Response, converting it to JSON
  4. Another .then() processes the actual data
  5. The .catch() handles any errors that might occur

Making Different Types of Requests

GET Request (Default)

javascript
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

javascript
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:

javascript
// 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

javascript
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));

Text Response

javascript
fetch('https://api.example.com/text')
.then(response => response.text())
.then(text => console.log(text));

Binary Data (Blob)

javascript
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:

javascript
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:

javascript
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:

javascript
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:

javascript
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:

html
<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():

javascript
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:

javascript
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:

bash
npm install whatwg-fetch

And then import it at the top of your JavaScript file:

javascript
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

  1. Create a simple app that fetches data from a public API (like JSONPlaceholder) and displays it in a list
  2. Build a form that posts data to a server and handles the response
  3. Create a pagination system using fetch to load more data when a user scrolls or clicks a button
  4. Implement a retry mechanism that attempts to fetch data again if the initial request fails
  5. Build a client for a REST API that handles CRUD operations using different fetch requests

Additional Resources

Happy coding!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)