🚀 Advanced HTTP Client

🚀 Advanced HTTP Client - Universal HTTP client for JavaScript and TypeScript

📋 1. Basic Usage - Static Methods

Simple GET, POST, PATCH, and DELETE requests

📝 Code Examples:

// GET request
const response = await HttpClient.get('https://jsonplaceholder.typicode.com/posts/1');
console.log('Data:', response.data);

// POST request
const postData = { title: 'Test Post', body: 'Content', userId: 1 };
const postResponse = await HttpClient.post('https://jsonplaceholder.typicode.com/posts', postData);

// PATCH request
const patchData = { title: 'Updated Title' };
const patchResponse = await HttpClient.patch('https://jsonplaceholder.typicode.com/posts/1', patchData);

// DELETE request
const deleteResponse = await HttpClient.delete('https://jsonplaceholder.typicode.com/posts/1');

âš™ī¸ 2. Instance Configuration

Create HttpClient instances with baseURL and default headers

📝 Code Examples:

// Create an instance with baseURL and headers
const api = HttpClient.create({
  baseURL: 'https://jsonplaceholder.typicode.com',
  headers: {
    'Authorization': 'Bearer your-token-here',
    'X-API-Version': '1.0',
    'Content-Type': 'application/json'
  }
});

// Use the instance (baseURL is automatically prepended)
const response = await api.get('/posts/2'); // GET https://jsonplaceholder.typicode.com/posts/2

// POST with instance defaults
const newPost = { title: 'Instance Post', body: 'Content', userId: 1 };
const postResponse = await api.post('/posts', newPost);

🌐 3. Global Headers

Set headers that apply to all requests

📝 Code Examples:

// Set global headers that apply to all requests
HttpClient.setHeader('X-Global-Header', 'global-value');
HttpClient.setHeader('User-Agent', 'advanced-http-client/1.0.0');

// These headers will be sent with every request
const response = await HttpClient.get('https://httpbin.org/headers');
console.log('Headers sent:', response.data.headers);

// Global headers can be overridden by per-request headers
const response2 = await HttpClient.get('https://httpbin.org/headers', {
  headers: { 'X-Global-Header': 'override-value' }
});

📊 4. Header Precedence

Test header priority: request > instance > global

📝 Code Examples:

// Header precedence: request > instance > global

// Set global header
HttpClient.setHeader('X-Header', 'global-value');

// Create instance with different header value
const api = HttpClient.create({
  headers: {
    'X-Header': 'instance-value',
    'X-Instance-Only': 'instance-only'
  }
});

// Request headers override instance and global
const response = await api.get('https://httpbin.org/headers', {
  headers: {
    'X-Header': 'request-value',        // Overrides instance and global
    'X-Request-Only': 'request-only'    // New header
  }
});

// Result: X-Header = 'request-value' (request wins)
// Result: X-Instance-Only = 'instance-only' (from instance)
// Result: X-Request-Only = 'request-only' (from request)

🔒 5. Isolated Requests

Requests that ignore global and instance settings

📝 Code Examples:

// Completely isolated request - ignores all global/instance settings
const response = await HttpClient.post(
  'https://httpbin.org/post',
  { message: 'isolated request' },
  { 
    isolated: true,
    headers: { 'X-Isolated': 'isolated-only' }
  }
);

// Isolated request with selective header inclusion
const api = HttpClient.create({
  baseURL: 'https://jsonplaceholder.typicode.com',
  headers: { 'Authorization': 'Bearer token' }
});

const selectiveResponse = await api.post(
  'https://jsonplaceholder.typicode.com/posts', // Use full URL in isolated mode
  { title: 'Selective Headers Post' },
  {
    isolated: true,
    includeHeaders: ['Authorization'], // Only include this header from global/instance
    headers: { 'X-Selective': 'selective-only' }
  }
);

âš ī¸ 6. Error Handling

Handle different types of HTTP errors

📝 Code Examples:

// Error handling with try-catch
try {
  const response = await HttpClient.get('https://jsonplaceholder.typicode.com/posts/1');
  console.log('Success:', response.data);
} catch (error) {
  if (error.response) {
    // HTTP error (4xx, 5xx)
    console.error('HTTP Error:', error.response.status, error.response.statusText);
    console.error('Response data:', error.response.data);
    console.error('Response headers:', error.response.headers);
  } else {
    // Network error or other issues
    console.error('Network Error:', error.message);
  }
}

// TypeScript error handling
try {
  await HttpClient.get('/api/data');
} catch (error) {
  if (error && typeof error === 'object' && 'response' in error) {
    const httpError = error as HttpClientError;
    console.error('Status:', httpError.response.status);
  }
}

📄 7. Content Type Handling

Automatic parsing of different response types

📝 Code Examples:

// Automatic content type parsing
const response = await HttpClient.get('https://jsonplaceholder.typicode.com/posts/1');

// JSON response (application/json)
console.log('Content-Type:', response.headers['content-type']);
console.log('Data type:', typeof response.data); // 'object'
console.log('Data:', response.data); // Parsed JSON object

// Text response (text/plain, text/html, etc.)
const textResponse = await HttpClient.get('https://httpbin.org/robots.txt');
console.log('Data type:', typeof textResponse.data); // 'string'
console.log('Data:', textResponse.data); // Raw text

// Form data response
const formResponse = await HttpClient.get('https://httpbin.org/forms/post');
console.log('Data type:', typeof formResponse.data); // 'string' (HTML)
console.log('Data:', formResponse.data); // HTML content

// The library automatically detects content type and parses accordingly:
// - application/json → JSON.parse()
// - text/* → response.text()
// - form/* → response.formData()
// - blob/* → response.blob()
// - arraybuffer → response.arrayBuffer()

🔧 8. Advanced Options

Custom headers, DELETE with body, and instance options

📝 Code Examples:

// Custom headers
const response = await HttpClient.get('https://httpbin.org/headers', {
  headers: {
    'X-Custom-Header': 'custom-value',
    'Accept': 'application/json'
  }
});

// DELETE with body
const deleteResponse = await HttpClient.delete('https://httpbin.org/delete', {
  message: 'This is a delete request with a body'
});

// Instance with custom options
const api = HttpClient.create({ baseURL: 'https://httpbin.org' });
const response = await api.get('/user-agent', {
  headers: { 'X-Instance-Custom': 'instance-custom-value' }
});

🔄 9. Interceptors

Request, response, and error interceptors for request/response transformation

📝 Code Examples:

// Create instance with interceptors
const api = HttpClient.create({
  baseURL: 'https://jsonplaceholder.typicode.com'
});

// Request interceptor - add auth token
api.interceptors.request.use(
  (config) => {
    config.headers['Authorization'] = 'Bearer token-123';
    console.log('Request interceptor:', config.url);
    return config;
  },
  (error) => {
    console.error('Request interceptor error:', error);
    return Promise.reject(error);
  }
);

// Response interceptor - transform data
api.interceptors.response.use(
  (response) => {
    console.log('Response interceptor:', response.status);
    response.data = { ...response.data, intercepted: true };
    return response;
  },
  (error) => {
    console.error('Response interceptor error:', error);
    return Promise.reject(error);
  }
);

// Error interceptor - handle specific errors
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.status === 401) {
      console.log('Unauthorized - redirecting to login');
    }
    return Promise.reject(error);
  }
);

🌍 10. Real-World API Example

JSONPlaceholder API integration (CORS-friendly)

📝 Code Examples:

// Real-world API integration (CORS-friendly)
const api = HttpClient.create({
  baseURL: 'https://jsonplaceholder.typicode.com',
  headers: {
    'Accept': 'application/json',
    'User-Agent': 'advanced-http-client-example'
  }
});

// Get user information
const user = await api.get('/users/1');
console.log('Username:', user.data.username);
console.log('Name:', user.data.name);
console.log('Email:', user.data.email);

// Get posts list
const posts = await api.get('/posts');
console.log('Post count:', posts.data.length);
posts.data.slice(0, 3).forEach(post => {
  console.log(`- ${post.title.substring(0, 50)}...`);
});

// Create a new post
const newPost = await api.post('/posts', {
  title: 'New Post',
  body: 'Post content',
  userId: 1
});
console.log('Created post ID:', newPost.data.id);

⚡ 11. Performance Test

Concurrent requests and performance metrics

📝 Code Examples:

// Performance testing with concurrent requests
const api = HttpClient.create({
  baseURL: 'https://jsonplaceholder.typicode.com'
});

// Make multiple concurrent requests
const startTime = Date.now();
const promises = [
  api.get('/posts/1'),
  api.get('/posts/2'),
  api.get('/posts/3'),
  api.get('/posts/4'),
  api.get('/posts/5')
];

const responses = await Promise.all(promises);
const endTime = Date.now();

console.log(`Completed ${responses.length} requests in ${endTime - startTime}ms`);
console.log(`Average time per request: ${Math.round((endTime - startTime) / responses.length)}ms`);

// Process responses
responses.forEach((response, index) => {
  console.log(`Request ${index + 1}: ${response.data.title.substring(0, 30)}...`);
});

// Sequential vs concurrent performance
// Sequential (slower):
for (let i = 1; i <= 5; i++) {
  await api.get(`/posts/${i}`);
}

// Concurrent (faster):
const concurrentPromises = Array.from({length: 5}, (_, i) => 
  api.get(`/posts/${i + 1}`)
);
await Promise.all(concurrentPromises);

âąī¸ 12. Timeout Example

Abort a request if it exceeds the specified timeout

📝 Code Example:

// Per-request timeout
try {
  await HttpClient.get('https://httpbin.org/delay/5', { timeout: 2000 });
} catch (err) {
  console.log('Request aborted (per-request):', err.message);
}

// Instance-level default timeout
const apiTimeout = HttpClient.create({ baseURL: 'https://httpbin.org', timeout: 2000 });
try {
  await apiTimeout.get('/delay/5');
} catch (err) {
  console.log('Request aborted (instance default):', err.message);
}

đŸšĢ 13. Cancellation Example

Start requests with controlKey and cancel them programmatically

📝 Code Example:

// Start a cancellable request (with controlKey)
const ctrlKey = HttpClient.generateControlKey();
const withKey = HttpClient.get('https://httpbin.org/delay/5', { controlKey: ctrlKey });

// Later, cancel that specific one
HttpClient.cancelRequest(ctrlKey);

// Start a request WITHOUT a controlKey
const noKey = HttpClient.get('https://httpbin.org/delay/5');

// Cancel every pending request (with or without keys)
HttpClient.cancelAllRequests();