|
The Fetch API is built into Node.js for making HTTP requests. It's the same API used in browsers, making it easy to write isomorphic code.
|
|
|
Basic GET Request
fetch() returns a Promise that resolves to a Response object.
|
async function basicGet() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const data = await response.json();
console.log('Status:', response.status);
console.log('Data:', data);
}
basicGet();
|
|
Checking Response Status
Always check if the response was successful. fetch() doesn't throw on HTTP errors.
|
async function checkStatus() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
return data;
}
|
|
POST Request with JSON Body
Send data by setting method, headers, and body.
|
async function postJson() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'Hello',
body: 'World',
userId: 1
})
});
const data = await response.json();
console.log('Created:', data);
}
|
|
Other HTTP Methods
Use PUT, PATCH, DELETE the same way.
|
async function otherMethods() {
const putResponse = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Updated', body: 'Content', userId: 1 })
});
const patchResponse = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Patched Title' })
});
const deleteResponse = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'DELETE'
});
console.log('Delete status:', deleteResponse.status);
}
|
|
Custom Headers
Set headers using a plain object or Headers instance.
|
async function customHeaders() {
const response1 = await fetch('https://api.example.com/data', {
headers: {
'Authorization': 'Bearer token123',
'X-Custom-Header': 'value'
}
});
const headers = new Headers();
headers.append('Authorization', 'Bearer token123');
headers.append('Accept', 'application/json');
const response2 = await fetch('https://api.example.com/data', { headers });
}
|
|
Reading Response Headers
Access response headers through the headers property.
|
async function readHeaders() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
console.log('Content-Type:', response.headers.get('content-type'));
console.log('Date:', response.headers.get('date'));
for (const [key, value] of response.headers) {
console.log(`${key}: ${value}`);
}
}
|
|
Different Response Types
Response provides methods for different data formats.
|
async function responseTypes() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
}
|
|
Timeout with AbortController
Use AbortController to set a timeout for requests.
|
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 (err) {
clearTimeout(timeoutId);
if (err.name === 'AbortError') {
throw new Error('Request timed out');
}
throw err;
}
}
|
|
Cancelling Requests
Cancel in-flight requests using AbortController.
|
async function cancellableRequest() {
const controller = new AbortController();
const fetchPromise = fetch('https://jsonplaceholder.typicode.com/posts', {
signal: controller.signal
});
setTimeout(() => {
controller.abort();
console.log('Request cancelled');
}, 100);
try {
const response = await fetchPromise;
return await response.json();
} catch (err) {
if (err.name === 'AbortError') {
console.log('Fetch was aborted');
}
}
}
|
|
Streaming Response Body
Read large responses as a stream.
|
async function streamResponse() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value, { stream: true });
console.log('Received chunk:', text.length, 'bytes');
}
}
|
|
Error Handling
Handle network errors and HTTP errors separately.
|
async function errorHandling() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data;
} catch (err) {
if (err instanceof TypeError) {
console.error('Network error:', err.message);
} else {
console.error('Error:', err.message);
}
}
}
|
|
Parallel Requests
Use Promise.all() to make multiple requests concurrently.
|
async function parallelRequests() {
const urls = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3'
];
const responses = await Promise.all(urls.map(url => fetch(url)));
const data = await Promise.all(responses.map(r => r.json()));
console.log('All posts:', data);
}
|
|
Retry Logic
Implement retry logic for unreliable endpoints.
|
async function fetchWithRetry(url, maxRetries = 3, delay = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url);
if (response.ok) return response;
if (response.status >= 500 && attempt < maxRetries) {
console.log(`Retry ${attempt}/${maxRetries} after server error`);
await new Promise(r => setTimeout(r, delay * attempt));
continue;
}
throw new Error(`HTTP ${response.status}`);
} catch (err) {
if (attempt === maxRetries) throw err;
console.log(`Retry ${attempt}/${maxRetries} after error: ${err.message}`);
await new Promise(r => setTimeout(r, delay * attempt));
}
}
}
|
|
Complete Example: API Client
A reusable API client wrapper.
Usage
|
class ApiClient {
constructor(baseUrl, defaultHeaders = {}) {
this.baseUrl = baseUrl;
this.defaultHeaders = defaultHeaders;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
...options,
headers: {
'Content-Type': 'application/json',
...this.defaultHeaders,
...options.headers
}
};
if (options.body && typeof options.body === 'object') {
config.body = JSON.stringify(options.body);
}
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
get(endpoint) { return this.request(endpoint); }
post(endpoint, body) { return this.request(endpoint, { method: 'POST', body }); }
put(endpoint, body) { return this.request(endpoint, { method: 'PUT', body }); }
delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); }
}
const api = new ApiClient('https://jsonplaceholder.typicode.com');
api.get('/posts/1').then(console.log);
|