Making an API call with fetch and async/await:
async function getData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
Three lines handle the request, response, and parsing. This is simpler than XMLHttpRequest or callback-based approaches.
The fetch Function
fetch() returns a Promise that resolves to a Response object:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
But with async/await, it's clearer:
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
async and await Keywords
async declares a function that returns a Promise:
async function example() {
return 'hello';
}
example().then(value => console.log(value)); // 'hello'
await pauses execution until the Promise resolves:
async function example() {
const result = await someAsyncOperation();
console.log(result); // Runs after promise resolves
}
await only works inside async functions (or at the top level in modules).
Basic GET Request
async function fetchUsers() {
const response = await fetch('https://api.example.com/users');
const users = await response.json();
return users;
}
The first await gets the response. The second parses the JSON body.
Checking Response Status
Fetch doesn't reject on HTTP errors (404, 500). Check response.ok:
async function fetchData() {
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();
return data;
}
response.ok is true for status codes 200-299.
POST Requests
Pass options as the second argument:
async function createUser(userData) {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const newUser = await response.json();
return newUser;
}
// Usage
const user = await createUser({ name: 'Alice', email: 'alice@example.com' });
Other HTTP Methods
PUT, PATCH, DELETE work similarly:
// PUT - full update
await fetch(`https://api.example.com/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updatedUser)
});
// PATCH - partial update
await fetch(`https://api.example.com/users/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: newEmail })
});
// DELETE
await fetch(`https://api.example.com/users/${id}`, {
method: 'DELETE'
});
Error Handling
Use try/catch for async errors:
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();
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error; // Re-throw or handle as needed
}
}
This catches network errors and your thrown HTTP errors.
Response Methods
Different ways to parse the response body:
const data = await response.json(); // Parse JSON
const text = await response.text(); // Get text
const blob = await response.blob(); // Get binary data
const buffer = await response.arrayBuffer(); // Get ArrayBuffer
const form = await response.formData(); // Get FormData
Use .json() for APIs, .text() for HTML, .blob() for images.
Headers
Reading response headers:
const response = await fetch('https://api.example.com/data');
const contentType = response.headers.get('Content-Type');
const rateLimit = response.headers.get('X-RateLimit-Remaining');
Setting request headers:
await fetch('https://api.example.com/data', {
headers: {
'Authorization': 'Bearer token123',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
Authentication
Bearer token authentication:
async function fetchProtectedData(token) {
const response = await fetch('https://api.example.com/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
if (response.status === 401) {
throw new Error('Unauthorized - token may be expired');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
Query Parameters
Use URLSearchParams for query strings:
const params = new URLSearchParams({
page: 1,
limit: 10,
sort: 'name'
});
const response = await fetch(`https://api.example.com/users?${params}`);
Or build manually:
const url = `https://api.example.com/users?page=1&limit=10&sort=name`;
const response = await fetch(url);
Timeout Handling
Fetch doesn't have built-in timeout. Use AbortController:
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
signal: controller.signal
});
clearTimeout(timeoutId);
return response;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
Parallel Requests
Use Promise.all() for multiple requests:
async function fetchMultiple() {
const [users, posts, comments] = await Promise.all([
fetch('https://api.example.com/users').then(r => r.json()),
fetch('https://api.example.com/posts').then(r => r.json()),
fetch('https://api.example.com/comments').then(r => r.json())
]);
return { users, posts, comments };
}
All requests run concurrently, saving time.
Retrying Failed Requests
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
FormData for File Uploads
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('name', file.name);
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData // Don't set Content-Type - browser sets it with boundary
});
return await response.json();
}
CORS and Credentials
Include credentials (cookies) in cross-origin requests:
await fetch('https://api.example.com/data', {
credentials: 'include' // Send cookies
});
Options: 'omit' (no cookies), 'same-origin' (default), 'include' (always send).
fetch vs Axios
Fetch is built-in. Axios is a library with extras:
- Fetch requires
response.json(); Axios auto-parses - Fetch doesn't reject on HTTP errors; Axios does
- Axios has timeout built-in; fetch needs AbortController
- Axios supports request/response interceptors
For simple APIs, fetch is sufficient. For complex needs, Axios adds convenience.
Browser Support
Fetch works in all modern browsers. IE11 needs a polyfill. Async/await requires transpiling for IE11.
Further Reading
MDN's Fetch API documentation covers all options and methods.
The async function reference explains async/await behavior in detail.
Jake Archibald's introduction to fetch covers practical use cases and gotchas.
Fetch with async/await is the modern standard for HTTP in JavaScript.
0 comments