javascript
examples
examples.js⚡javascript
/**
* ========================================
* 11.3 Fetch API and HTTP - Examples
* ========================================
*
* Comprehensive examples of making HTTP requests with the Fetch API.
* Note: Most examples require an internet connection and a test API.
*/
/**
* EXAMPLE 1: Basic GET Request
*
* The simplest form of fetch - retrieving data.
*/
// Promise syntax
function basicFetchWithPromise(url) {
fetch(url)
.then((response) => {
console.log('Status:', response.status);
console.log('OK:', response.ok);
return response.json();
})
.then((data) => {
console.log('Data:', data);
})
.catch((error) => {
console.error('Error:', error);
});
}
// Async/await syntax (preferred)
async function basicFetchWithAsync(url) {
try {
const response = await fetch(url);
console.log('Status:', response.status);
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 failed:', error);
throw error;
}
}
// Test with: basicFetchWithAsync('https://jsonplaceholder.typicode.com/posts/1')
/**
* EXAMPLE 2: POST Request with JSON Body
*
* Sending data to a server.
*/
async function postJSON(url, data) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('POST failed:', error);
throw error;
}
}
// Usage:
// postJSON('https://jsonplaceholder.typicode.com/posts', {
// title: 'Hello',
// body: 'World',
// userId: 1
// }).then(console.log);
/**
* EXAMPLE 3: All HTTP Methods
*
* Template functions for each HTTP method.
*/
const httpMethods = {
async get(url) {
const response = await fetch(url);
return response.json();
},
async post(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
return response.json();
},
async put(url, data) {
const response = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
return response.json();
},
async patch(url, data) {
const response = await fetch(url, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
return response.json();
},
async delete(url) {
const response = await fetch(url, { method: 'DELETE' });
return response.ok;
},
};
/**
* EXAMPLE 4: Query Parameters
*
* Building URLs with query strings.
*/
function buildURL(baseUrl, params) {
const url = new URL(baseUrl);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.append(key, value);
}
});
return url.toString();
}
async function fetchWithParams(baseUrl, params) {
const url = buildURL(baseUrl, params);
console.log('Fetching:', url);
const response = await fetch(url);
return response.json();
}
// Usage:
// fetchWithParams('https://api.example.com/search', {
// query: 'javascript',
// page: 1,
// limit: 10
// });
/**
* EXAMPLE 5: Request Headers
*
* Working with HTTP headers.
*/
async function fetchWithHeaders(url, customHeaders = {}) {
// Create headers object
const headers = new Headers({
Accept: 'application/json',
'Accept-Language': 'en-US',
...customHeaders,
});
const response = await fetch(url, { headers });
// Reading response headers
console.log('Response Headers:');
response.headers.forEach((value, key) => {
console.log(` ${key}: ${value}`);
});
// Common header access
console.log('Content-Type:', response.headers.get('content-type'));
console.log('Content-Length:', response.headers.get('content-length'));
return response.json();
}
/**
* EXAMPLE 6: Authentication
*
* Common authentication patterns.
*/
// Basic Authentication
async function fetchWithBasicAuth(url, username, password) {
const credentials = btoa(`${username}:${password}`);
return fetch(url, {
headers: {
Authorization: `Basic ${credentials}`,
},
});
}
// Bearer Token Authentication
async function fetchWithBearerToken(url, token) {
return fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
},
});
}
// API Key Authentication
async function fetchWithApiKey(url, apiKey, headerName = 'X-API-Key') {
return fetch(url, {
headers: {
[headerName]: apiKey,
},
});
}
/**
* EXAMPLE 7: Error Handling
*
* Comprehensive error handling for fetch.
*/
class HTTPError extends Error {
constructor(response) {
super(`HTTP Error: ${response.status} ${response.statusText}`);
this.name = 'HTTPError';
this.status = response.status;
this.statusText = response.statusText;
this.response = response;
}
}
async function fetchWithErrorHandling(url, options = {}) {
let response;
try {
response = await fetch(url, options);
} catch (error) {
// Network error (offline, DNS failure, etc.)
if (error.name === 'TypeError') {
throw new Error('Network error: Unable to reach server');
}
throw error;
}
// HTTP error (4xx, 5xx)
if (!response.ok) {
const error = new HTTPError(response);
// Try to parse error message from response
try {
const errorBody = await response.json();
error.message = errorBody.message || error.message;
error.details = errorBody;
} catch {
// Response wasn't JSON
}
throw error;
}
return response;
}
// Usage with specific error handling
async function demonstrateErrorHandling(url) {
try {
const response = await fetchWithErrorHandling(url);
return await response.json();
} catch (error) {
if (error instanceof HTTPError) {
switch (error.status) {
case 401:
console.log('Unauthorized - please log in');
break;
case 403:
console.log('Forbidden - insufficient permissions');
break;
case 404:
console.log('Resource not found');
break;
case 429:
console.log('Too many requests - please slow down');
break;
case 500:
console.log('Server error - try again later');
break;
default:
console.log('HTTP Error:', error.status);
}
} else {
console.log('Network or other error:', error.message);
}
throw error;
}
}
/**
* EXAMPLE 8: Request Timeout with AbortController
*
* Cancel requests that take too long.
*/
async function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
});
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error(`Request timeout after ${timeout}ms`);
}
throw error;
}
}
// Modern approach using AbortSignal.timeout (newer browsers)
async function fetchWithTimeoutModern(url, options = {}, timeout = 5000) {
try {
return await fetch(url, {
...options,
signal: AbortSignal.timeout(timeout),
});
} catch (error) {
if (error.name === 'TimeoutError') {
throw new Error(`Request timeout after ${timeout}ms`);
}
throw error;
}
}
/**
* EXAMPLE 9: Cancellable Requests
*
* Allow user to cancel in-flight requests.
*/
function createCancellableRequest(url, options = {}) {
const controller = new AbortController();
const promise = fetch(url, {
...options,
signal: controller.signal,
});
return {
promise,
cancel: () => controller.abort(),
signal: controller.signal,
};
}
// Usage in a search component
class SearchComponent {
constructor() {
this.currentRequest = null;
}
async search(query) {
// Cancel previous request if still pending
if (this.currentRequest) {
this.currentRequest.cancel();
}
this.currentRequest = createCancellableRequest(
`https://api.example.com/search?q=${encodeURIComponent(query)}`
);
try {
const response = await this.currentRequest.promise;
const results = await response.json();
return results;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Search cancelled');
return null;
}
throw error;
} finally {
this.currentRequest = null;
}
}
}
/**
* EXAMPLE 10: Retry Logic
*
* Automatically retry failed requests.
*/
async function fetchWithRetry(url, options = {}, config = {}) {
const {
retries = 3,
delay = 1000,
backoff = 2,
retryOn = [500, 502, 503, 504],
} = config;
let lastError;
for (let attempt = 0; attempt < retries; attempt++) {
try {
const response = await fetch(url, options);
// Check if we should retry this status
if (!response.ok && retryOn.includes(response.status)) {
throw new Error(`HTTP ${response.status}`);
}
return response;
} catch (error) {
lastError = error;
console.log(`Attempt ${attempt + 1} failed:`, error.message);
if (attempt < retries - 1) {
const waitTime = delay * Math.pow(backoff, attempt);
console.log(`Waiting ${waitTime}ms before retry...`);
await new Promise((r) => setTimeout(r, waitTime));
}
}
}
throw new Error(`Failed after ${retries} attempts: ${lastError.message}`);
}
/**
* EXAMPLE 11: File Upload
*
* Uploading files with FormData.
*/
async function uploadFile(file, additionalData = {}) {
const formData = new FormData();
formData.append('file', file);
// Add additional fields
Object.entries(additionalData).forEach(([key, value]) => {
formData.append(key, value);
});
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData,
// Don't set Content-Type - browser sets it with boundary
});
if (!response.ok) {
throw new Error('Upload failed');
}
return response.json();
}
// Upload with progress (using XMLHttpRequest as fetch doesn't support upload progress)
function uploadWithProgress(file, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('file', file);
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
onProgress(percent);
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`Upload failed: ${xhr.status}`));
}
});
xhr.addEventListener('error', () => reject(new Error('Network error')));
xhr.open('POST', 'https://api.example.com/upload');
xhr.send(formData);
});
}
/**
* EXAMPLE 12: Download with Progress
*
* Track download progress using streams.
*/
async function downloadWithProgress(url, onProgress) {
const response = await fetch(url);
const contentLength = response.headers.get('content-length');
const total = parseInt(contentLength, 10);
const reader = response.body.getReader();
const chunks = [];
let received = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
received += value.length;
if (total) {
const percent = (received / total) * 100;
onProgress(percent, received, total);
}
}
// Combine chunks into single array
const blob = new Blob(chunks);
return blob;
}
// Usage:
// downloadWithProgress('https://example.com/large-file.zip', (percent) => {
// console.log(`Downloaded: ${percent.toFixed(1)}%`);
// });
/**
* EXAMPLE 13: Parallel Requests
*
* Fetch multiple resources simultaneously.
*/
async function fetchAll(urls) {
const responses = await Promise.all(urls.map((url) => fetch(url)));
// Check all responses
responses.forEach((response, index) => {
if (!response.ok) {
console.warn(`Request to ${urls[index]} failed: ${response.status}`);
}
});
// Parse all as JSON
return Promise.all(responses.map((response) => response.json()));
}
// With error handling - get all that succeed
async function fetchAllSettled(urls) {
const results = await Promise.allSettled(
urls.map(async (url) => {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
);
return results.map((result, index) => ({
url: urls[index],
status: result.status,
data: result.status === 'fulfilled' ? result.value : null,
error: result.status === 'rejected' ? result.reason : null,
}));
}
/**
* EXAMPLE 14: Sequential Requests
*
* Execute requests one after another.
*/
async function fetchSequential(urls) {
const results = [];
for (const url of urls) {
const response = await fetch(url);
const data = await response.json();
results.push(data);
}
return results;
}
// With dependency - each request uses data from previous
async function fetchWithDependencies(initialUrl) {
// First request
const userResponse = await fetch(initialUrl);
const user = await userResponse.json();
// Second request uses first result
const postsResponse = await fetch(`/users/${user.id}/posts`);
const posts = await postsResponse.json();
// Third request uses second result
const commentsPromises = posts.map((post) =>
fetch(`/posts/${post.id}/comments`).then((r) => r.json())
);
const comments = await Promise.all(commentsPromises);
return { user, posts, comments };
}
/**
* EXAMPLE 15: API Client Factory
*
* Create a reusable API client with common configuration.
*/
function createAPIClient(baseURL, defaultHeaders = {}) {
async function request(endpoint, options = {}) {
const url = new URL(endpoint, baseURL);
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...defaultHeaders,
...options.headers,
},
});
if (!response.ok) {
const error = new Error(`HTTP ${response.status}`);
error.status = response.status;
try {
error.data = await response.json();
} catch {}
throw error;
}
// Return null for 204 No Content
if (response.status === 204) return null;
return response.json();
}
return {
get: (endpoint, options) =>
request(endpoint, { ...options, method: 'GET' }),
post: (endpoint, data, options) =>
request(endpoint, {
...options,
method: 'POST',
body: JSON.stringify(data),
}),
put: (endpoint, data, options) =>
request(endpoint, {
...options,
method: 'PUT',
body: JSON.stringify(data),
}),
patch: (endpoint, data, options) =>
request(endpoint, {
...options,
method: 'PATCH',
body: JSON.stringify(data),
}),
delete: (endpoint, options) =>
request(endpoint, { ...options, method: 'DELETE' }),
};
}
// Usage:
// const api = createAPIClient('https://api.example.com', {
// 'Authorization': 'Bearer token123'
// });
//
// await api.get('/users');
// await api.post('/users', { name: 'John' });
// await api.put('/users/1', { name: 'Jane' });
// await api.delete('/users/1');
/**
* EXAMPLE 16: Response Caching
*
* Simple in-memory cache for API responses.
*/
class CachedFetch {
constructor(options = {}) {
this.cache = new Map();
this.ttl = options.ttl || 5 * 60 * 1000; // 5 minutes default
}
generateKey(url, options = {}) {
return `${options.method || 'GET'}-${url}`;
}
isExpired(entry) {
return Date.now() > entry.expires;
}
async fetch(url, options = {}) {
const key = this.generateKey(url, options);
const cached = this.cache.get(key);
// Return cached if valid
if (cached && !this.isExpired(cached)) {
console.log('Cache hit:', url);
return cached.data;
}
// Fetch fresh data
console.log('Cache miss:', url);
const response = await fetch(url, options);
const data = await response.json();
// Cache the result
this.cache.set(key, {
data,
expires: Date.now() + this.ttl,
});
return data;
}
invalidate(url, options = {}) {
const key = this.generateKey(url, options);
this.cache.delete(key);
}
clear() {
this.cache.clear();
}
}
/**
* EXAMPLE 17: Request Queue
*
* Limit concurrent requests to prevent overwhelming the server.
*/
class RequestQueue {
constructor(maxConcurrent = 3) {
this.maxConcurrent = maxConcurrent;
this.running = 0;
this.queue = [];
}
async add(fetchFn) {
return new Promise((resolve, reject) => {
this.queue.push({ fetchFn, resolve, reject });
this.process();
});
}
async process() {
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
return;
}
this.running++;
const { fetchFn, resolve, reject } = this.queue.shift();
try {
const result = await fetchFn();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.process();
}
}
}
// Usage:
// const queue = new RequestQueue(2);
//
// urls.forEach(url => {
// queue.add(() => fetch(url).then(r => r.json()))
// .then(data => console.log('Loaded:', url));
// });
/**
* EXAMPLE 18: Streaming JSON
*
* Parse large JSON responses incrementally.
*/
async function streamLargeJSON(url, onItem) {
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let inString = false;
let braceCount = 0;
let objectStart = -1;
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Parse objects from buffer
for (let i = 0; i < buffer.length; i++) {
const char = buffer[i];
// Handle strings (to avoid counting braces in strings)
if (char === '"' && buffer[i - 1] !== '\\') {
inString = !inString;
}
if (!inString) {
if (char === '{') {
if (braceCount === 0) objectStart = i;
braceCount++;
} else if (char === '}') {
braceCount--;
if (braceCount === 0 && objectStart !== -1) {
const jsonStr = buffer.substring(objectStart, i + 1);
try {
const obj = JSON.parse(jsonStr);
onItem(obj);
} catch (e) {
// Invalid JSON, skip
}
objectStart = -1;
}
}
}
}
// Keep unprocessed data in buffer
if (objectStart !== -1) {
buffer = buffer.substring(objectStart);
objectStart = 0;
} else {
buffer = '';
}
}
}
console.log('Fetch API examples loaded!');
console.log(
'Try: basicFetchWithAsync("https://jsonplaceholder.typicode.com/posts/1")'
);