Now.js Framework Documentation

Now.js Framework Documentation

HttpClient - Low-level HTTP Client

EN 31 Oct 2025 01:22

HttpClient - Low-level HTTP Client

Documentation for HttpClient, the Low-level HTTP Client of Now.js Framework

📋 Table of Contents

  1. Overview
  2. Installation and Import
  3. All Exports
  4. HttpClient Class
  5. http Instance
  6. httpThrow Instance
  7. simpleFetch Utility
  8. Interceptors
  9. CSRF Protection
  10. Security Features
  11. Error Handling
  12. Usage Examples
  13. API Reference
  14. Best Practices

Overview

HttpClient is a Low-level HTTP Client built on the Fetch API with modern and secure features, suitable for API calls that require low-level control.

Key Features

  • 4 Exports: HttpClient, http, httpThrow, simpleFetch
  • Interceptors: Request and Response interceptors
  • CSRF Protection: Prevent CSRF attacks
  • Auto Content-Type Detection: Detect and parse response automatically
  • Timeout Support: Set timeout for requests
  • Error Handling: Flexible error management
  • Custom Response Handler: Define custom response handling
  • AbortController Support: Cancel requests
  • FormData/Blob/ArrayBuffer Support: Support various content types

Installation and Import

HttpClient is loaded with Now.js Framework and ready to use immediately via window object:

// No import needed - ready to use
console.log(window.http);        // http instance
console.log(window.httpThrow);   // httpThrow instance
console.log(window.simpleFetch); // simpleFetch utility
console.log(window.HttpClient);  // HttpClient class

All Exports

HttpClient provides 4 main exports:

Export Type throwOnError Use Case
HttpClient Class As configured Create custom instance
http Instance false General use (safe mode)
httpThrow Instance true Use when need to throw errors
simpleFetch Utility Object N/A Lightweight and simple usage

HttpClient Class

Constructor

Create new HttpClient instance:

const client = new HttpClient(options);

Options

Option Type Default Description
baseURL string '' Base URL for all requests
headers object {} Default headers
timeout number 30000 Timeout in milliseconds
throwOnError boolean true Throw error on failed response
responseHandler function null Custom response handler
csrfCookieName string 'XSRF-TOKEN' CSRF cookie name
csrfTokenSelector string 'meta[name="csrf-token"]' CSS selector for CSRF meta tag
security object {} Security configuration

Instance Creation Examples

// Basic instance
const api = new HttpClient({
  baseURL: 'https://api.example.com',
  timeout: 10000
});

// Instance with CSRF protection
const secureApi = new HttpClient({
  baseURL: 'https://api.example.com',
  security: {
    csrf: {
      enabled: true,
      tokenName: '_token',
      headerName: 'X-CSRF-Token'
    }
  }
});

// Instance with custom response handler
const customApi = new HttpClient({
  baseURL: 'https://api.example.com',
  responseHandler: async (response) => {
    const data = await response.json();
    return {
      success: response.ok,
      result: data,
      timestamp: Date.now()
    };
  }
});

Methods

HTTP Methods

// GET request
await client.get(url, options);

// POST request
await client.post(url, data, options);

// PUT request
await client.put(url, data, options);

// DELETE request
await client.delete(url, options);

// PATCH request
await client.patch(url, data, options);

// HEAD request
await client.head(url, options);

// OPTIONS request
await client.options(url, options);

Safe Methods (don't throw error)

// Safe GET
await client.getSafe(url, options);

// Safe POST
await client.postSafe(url, data, options);

// Safe PUT
await client.putSafe(url, data, options);

// Safe DELETE
await client.deleteSafe(url, options);

Upload Method

// Upload single file
await client.upload('/api/upload', file);

// Upload multiple files
await client.upload('/api/upload', [file1, file2]);

// Upload with additional data
await client.upload('/api/upload', files, {
  data: {
    title: 'My Upload',
    category: 'images'
  }
});

Configuration Methods

// Set base URL
client.setBaseURL('https://api.example.com');

// Set default header
client.setDefaultHeader('Authorization', 'Bearer token');

// Set default headers
client.setDefaultHeaders({
  'X-App-Version': '1.0.0',
  'X-Device-ID': 'abc123'
});

// Set timeout
client.setTimeout(60000); // 60 seconds

// Set CSRF token
client.setCsrfToken('token-value');

http Instance

Ready-to-use instance with throwOnError: false suitable for general use

Characteristics

  • ❌ Doesn't throw errors (need to check response.ok yourself)
  • ✅ Has timestamp and requestId in response
  • ✅ Auto-parse response by Content-Type

Usage

// GET request
const response = await http.get('/api/users');

if (response.ok) {
  console.log(response.data);
  console.log('Timestamp:', response.timestamp);
  console.log('Request ID:', response.requestId);
} else {
  console.error('Error:', response.statusText);
}

// POST request
const response = await http.post('/api/users', {
  name: 'John Doe',
  email: 'john@example.com'
});

if (response.ok) {
  console.log('Created:', response.data);
}

Response Object

{
  ok: boolean,              // true if status 200-299
  status: number,           // HTTP status code
  statusText: string,       // HTTP status text
  data: any,                // Parsed response data
  headers: object,          // Response headers
  url: string,              // Request URL
  timestamp: number,        // Timestamp (http instance only)
  requestId: string|null    // Request ID from header (http instance only)
}

httpThrow Instance

Ready-to-use instance with throwOnError: true suitable for use with try-catch

Characteristics

  • ✅ Throws HttpError on failed response
  • ✅ Has timestamp and requestId in response
  • ✅ Return format slightly different from http

Usage

try {
  // GET request
  const response = await httpThrow.get('/api/users');
  console.log(response.data);
  console.log('Message:', response.message);

} catch (error) {
  console.error('Error:', error.message);
  console.error('Status:', error.status);
  console.error('Response:', error.response);
}

// POST request
try {
  const response = await httpThrow.post('/api/users', {
    name: 'John Doe',
    email: 'john@example.com'
  });
  console.log('Created:', response.data);

} catch (error) {
  if (error.status === 422) {
    console.error('Validation errors:', error.response.data);
  } else {
    console.error('Server error:', error.message);
  }
}

Response Object (Success)

{
  status: number,           // HTTP status code
  data: any,                // Parsed response data
  message: string,          // Message from server or statusText
  meta: any,                // Meta data from server (if any)
  timestamp: number,        // Timestamp
  headers: object,          // Response headers
  requestId: string|null    // Request ID from header
}

HttpError Object (Error)

{
  name: 'HttpError',
  message: string,          // Error message
  status: number,           // HTTP status code
  response: object,         // Full response object
  timestamp: number         // Error timestamp
}

simpleFetch Utility

Lightweight fetch wrapper designed for easy and lightweight use, suitable for simple API calls

Characteristics

  • ✅ Zero dependencies (uses fetch API only)
  • ✅ Auto-parsing response (JSON, Text, Blob)
  • ✅ Convenience methods (.json(), .text(), .postJson())
  • ✅ Auto-attach bearer token from localStorage
  • ✅ Timeout support (default 15 seconds)
  • ✅ Method chaining for configuration

HTTP Methods

Standard Methods (return response object)

// GET request
const response = await simpleFetch.get('/api/users');
if (response.ok) {
  console.log(response.data);
}

// POST request
const response = await simpleFetch.post('/api/users', {
  name: 'John Doe'
});

// PUT request
const response = await simpleFetch.put('/api/users/1', {
  name: 'Jane Doe'
});

// DELETE request
const response = await simpleFetch.delete('/api/users/1');

// PATCH request
const response = await simpleFetch.patch('/api/users/1', {
  status: 'active'
});

Convenience Methods (return data directly)

// Get JSON data directly
const users = await simpleFetch.json('/api/users');
console.log(users); // Array of users or null

// Get text data directly
const html = await simpleFetch.text('/api/template');
console.log(html); // HTML string or null

// Post JSON and get data directly
const newUser = await simpleFetch.postJson('/api/users', {
  name: 'John Doe'
});
console.log(newUser); // Created user object or null

File Upload

// Upload single file
const response = await simpleFetch.upload('/api/upload', file);

// Upload multiple files
const response = await simpleFetch.upload('/api/upload', [file1, file2]);

// Upload with additional data
const response = await simpleFetch.upload('/api/upload', files, {
  title: 'My Photos',
  category: 'vacation'
});

// Upload with FormData
const formData = new FormData();
formData.append('file', file);
formData.append('title', 'Photo');
const response = await simpleFetch.upload('/api/upload', formData);

Configuration

// Set base URL
simpleFetch.setBaseURL('https://api.example.com');

// Set default headers
simpleFetch.setHeaders({
  'X-App-Version': '1.0.0',
  'X-Device-ID': 'abc123'
});

// Set single header
simpleFetch.setHeader('X-Custom-Header', 'value');

// Set timeout
simpleFetch.setTimeout(30000); // 30 seconds

// Remove header
simpleFetch.removeHeader('X-Custom-Header');

// Reset configuration
simpleFetch.resetConfig();

// Method chaining
simpleFetch
  .setBaseURL('https://api.example.com')
  .setTimeout(10000)
  .setHeader('X-App-Version', '1.0.0');

Auto Bearer Token

simpleFetch automatically retrieves bearer token from localStorage.getItem('auth_token') and adds it to Authorization header:

// If token exists in localStorage
localStorage.setItem('auth_token', 'your-token');

// simpleFetch will automatically add this header:
// Authorization: Bearer your-token

const response = await simpleFetch.get('/api/user/profile');

Response Object

{
  ok: boolean,              // true if status 200-299
  status: number,           // HTTP status code
  statusText: string,       // HTTP status text
  data: any,                // Parsed response data (or null)
  headers: object,          // Response headers
  url: string,              // Request URL
  error: Error              // Error object (if error)
}

Interceptors

Interceptors allow you to modify requests or responses before they are sent or received

Request Interceptor

Modify request config before sending:

// Add request interceptor
const removeInterceptor = http.addRequestInterceptor(config => {
  // Modify config
  config.headers['X-Timestamp'] = Date.now();
  config.headers['X-User-Agent'] = navigator.userAgent;

  // Must return config
  return config;
});

// Remove interceptor
removeInterceptor();

Response Interceptor

Handle response after receiving from server:

// Add response interceptor
const removeInterceptor = http.addResponseInterceptor(
  // onFulfilled - handle success response
  (response) => {
    console.log('Response received:', response.status);

    // Modify response if needed
    response.receivedAt = Date.now();

    return response;
  },

  // onRejected - handle error response
  (error) => {
    console.error('Response error:', error);

    // Modify error or throw
    if (error.status === 401) {
      // Redirect to login
      window.location.href = '/login';
    }

    throw error;
  }
);

// Remove interceptor
removeInterceptor();

Interceptor Usage Examples

1. Add Authentication Token

http.addRequestInterceptor(config => {
  const token = localStorage.getItem('auth_token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  return config;
});

2. Logging

// Log requests
http.addRequestInterceptor(config => {
  console.log(`[${config.method}] ${config.url}`);
  return config;
});

// Log responses
http.addResponseInterceptor(
  response => {
    console.log(`[${response.status}] ${response.url}`);
    return response;
  },
  error => {
    console.error(`[Error] ${error.status} - ${error.message}`);
    throw error;
  }
);

3. Retry on Network Error

http.addResponseInterceptor(
  response => response,
  async (error) => {
    if (error.status === 0 || error.status === 408) {
      console.log('Network error, retrying...');

      // Retry request
      return await http.request(error.url);
    }
    throw error;
  }
);

4. Transform Response

http.addResponseInterceptor(response => {
  // Transform response format
  return {
    success: response.ok,
    result: response.data,
    errors: response.ok ? null : response.data?.errors,
    timestamp: Date.now()
  };
});

5. Rate Limiting

const requestTimestamps = [];

http.addRequestInterceptor(config => {
  const now = Date.now();

  // Remove timestamps older than 1 minute
  const oneMinuteAgo = now - 60000;
  while (requestTimestamps.length && requestTimestamps[0] < oneMinuteAgo) {
    requestTimestamps.shift();
  }

  // Check rate limit (max 60 requests/minute)
  if (requestTimestamps.length >= 60) {
    throw new Error('Rate limit exceeded. Please wait.');
  }

  requestTimestamps.push(now);
  return config;
});

CSRF Protection

HttpClient has built-in CSRF protection to prevent Cross-Site Request Forgery attacks

Enable CSRF

const client = new HttpClient({
  security: {
    csrf: {
      enabled: true,
      tokenName: '_token',
      headerName: 'X-CSRF-Token'
    }
  }
});

How It Works

CSRF token is retrieved from:

  1. Meta tag (<meta name="csrf-token" content="token-value">)
  2. Cookie (name XSRF-TOKEN by default)
<!-- In <head> section -->
<meta name="csrf-token" content="your-csrf-token-here">

Auto-Attach CSRF Token

Token is automatically attached in:

  • Header: X-CSRF-Token: token-value
  • Request Body: For POST/PUT/DELETE requests
// Token will be attached automatically
await client.post('/api/delete-item', { id: 123 });

// Request will have:
// Header: X-CSRF-Token: your-token
// Body: { id: 123, _token: 'your-token' }

Set CSRF Token Manually

// Set token manually
client.setCsrfToken('new-token-value');

// Token will be used in subsequent requests
await client.post('/api/action', data);

CSRF Token Renewal

HttpClient automatically updates token from response header:

// Server sends header: X-CSRF-Token: new-token
const response = await client.post('/api/action', data);

// client.csrfToken will be automatically updated to 'new-token'

Security Features

HttpClient has several security features that can be enabled

1. CSRF Protection

Prevent Cross-Site Request Forgery:

const client = new HttpClient({
  security: {
    csrf: {
      enabled: true,
      tokenName: '_token',
      headerName: 'X-CSRF-Token'
    }
  }
});

2. Rate Limiting

Limit number of requests (requires SecurityManager):

const client = new HttpClient({
  security: {
    rateLimit: {
      enabled: true
    }
  }
});

3. Request Validation

Sanitize request data (requires SecurityManager):

const client = new HttpClient({
  security: {
    validation: {
      enabled: true
    }
  }
});

4. Timeout

Prevent long-hanging requests:

const client = new HttpClient({
  timeout: 10000 // 10 seconds
});

// Or set later
client.setTimeout(5000); // 5 seconds

5. Same-Origin Credentials

Prevent sending credentials across domains:

const client = new HttpClient();

// Default: credentials: 'same-origin'
await client.get('/api/data');

// Need to send credentials across origin
await client.get('https://api.example.com/data', {
  credentials: 'include'
});

Error Handling

HttpClient has multiple ways to handle errors

1. Safe Mode (http instance)

Use http instance that doesn't throw errors:

const response = await http.get('/api/users');

if (response.ok) {
  console.log('Success:', response.data);
} else {
  console.error('Error:', response.status, response.statusText);

  // Show error message from server
  if (response.data?.message) {
    console.error('Server message:', response.data.message);
  }
}

2. Throw Mode (httpThrow instance)

Use httpThrow instance that throws errors:

try {
  const response = await httpThrow.get('/api/users');
  console.log('Success:', response.data);

} catch (error) {
  console.error('Error:', error.message);
  console.error('Status:', error.status);

  // Handle error by status code
  switch (error.status) {
    case 401:
      // Redirect to login
      window.location.href = '/login';
      break;

    case 403:
      alert('Access denied');
      break;

    case 404:
      alert('Resource not found');
      break;

    case 422:
      // Validation errors
      const errors = error.response.data.errors;
      console.error('Validation errors:', errors);
      break;

    case 500:
      alert('Server error. Please try again later.');
      break;

    default:
      alert('An error occurred: ' + error.message);
  }
}

3. Custom Instance

Create instance with custom error handling:

const client = new HttpClient({
  throwOnError: false // or true as needed
});

// Override throwOnError per request
const response = await client.get('/api/users', {
  throwOnError: true // Throw error only for this request
});

4. Error Interceptor

Use response interceptor to handle errors:

http.addResponseInterceptor(
  response => response,
  error => {
    // Log error
    console.error('API Error:', error);

    // Show notification
    if (window.NotificationManager) {
      NotificationManager.error(error.message);
    }

    // Redirect on auth error
    if (error.status === 401) {
      window.location.href = '/login';
      return; // Don't throw
    }

    // Re-throw other errors
    throw error;
  }
);

5. Timeout Errors

Handle timeout errors:

const response = await http.get('/api/slow-endpoint');

if (!response.ok && response.status === 408) {
  console.error('Request timeout');
  alert('The server is taking too long to respond. Please try again.');
}

HttpError Object

try {
  await httpThrow.get('/api/users');
} catch (error) {
  console.log(error.name);      // 'HttpError'
  console.log(error.message);   // Error message
  console.log(error.status);    // HTTP status code
  console.log(error.response);  // Full response object
  console.log(error.timestamp); // Error timestamp
}

Usage Examples

1. Simple API Call

// Using simpleFetch
const users = await simpleFetch.json('/api/users');
console.log(users);

// Using http
const response = await http.get('/api/users');
if (response.ok) {
  console.log(response.data);
}

2. Send JSON Data

// POST
const response = await http.post('/api/users', {
  name: 'John Doe',
  email: 'john@example.com',
  age: 30
});

if (response.ok) {
  console.log('Created:', response.data);
}

// PUT
const response = await http.put('/api/users/1', {
  name: 'Jane Doe',
  email: 'jane@example.com'
});

// PATCH
const response = await http.patch('/api/users/1', {
  status: 'active'
});

3. File Upload

// Single file upload
const fileInput = document.querySelector('#file');
const file = fileInput.files[0];

const response = await http.upload('/api/upload', file);

if (response.ok) {
  console.log('Uploaded:', response.data.url);
}

// Multiple files upload
const files = Array.from(fileInput.files);
const response = await http.upload('/api/upload', files);

// Upload with additional data
const response = await http.upload('/api/upload', file, {
  data: {
    title: 'Profile Picture',
    category: 'avatars'
  }
});

4. Using FormData

const formData = new FormData();
formData.append('name', 'John Doe');
formData.append('email', 'john@example.com');
formData.append('avatar', file);

const response = await http.post('/api/users', formData);

5. File Download

const response = await http.get('/api/reports/download', {
  headers: {
    'Accept': 'application/pdf'
  }
});

if (response.ok) {
  // response.data will be Blob
  const blob = response.data;

  // Create download link
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'report.pdf';
  a.click();
  URL.revokeObjectURL(url);
}

6. Using Query Parameters

// Manual
const response = await http.get('/api/users?page=1&limit=10&sort=name');

// With URLSearchParams
const params = new URLSearchParams({
  page: 1,
  limit: 10,
  sort: 'name'
});

const response = await http.get(`/api/users?${params}`);

7. Cancel Request

const controller = new AbortController();

// Start request
const responsePromise = http.get('/api/large-data', {
  signal: controller.signal
});

// Cancel after 5 seconds
setTimeout(() => {
  controller.abort();
}, 5000);

try {
  const response = await responsePromise;
  console.log(response.data);
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Request cancelled');
  }
}

8. Using Custom Headers

const response = await http.get('/api/data', {
  headers: {
    'X-Custom-Header': 'value',
    'Accept-Language': 'en',
    'X-App-Version': '1.0.0'
  }
});

9. Using Authentication

// Bearer token
http.setDefaultHeader('Authorization', 'Bearer your-token-here');

// Or use interceptor
http.addRequestInterceptor(config => {
  const token = localStorage.getItem('auth_token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  return config;
});

// Basic auth
const username = 'user';
const password = 'pass';
const credentials = btoa(`${username}:${password}`);

http.setDefaultHeader('Authorization', `Basic ${credentials}`);

10. Using Base URL

// Set base URL
http.setBaseURL('https://api.example.com');

// Usage
await http.get('/users');        // https://api.example.com/users
await http.get('/posts');        // https://api.example.com/posts

// Override base URL with absolute URL
await http.get('https://other-api.com/data'); // https://other-api.com/data

11. Parallel Requests

// Wait for all requests to complete
const [users, posts, comments] = await Promise.all([
  http.get('/api/users'),
  http.get('/api/posts'),
  http.get('/api/comments')
]);

console.log('Users:', users.data);
console.log('Posts:', posts.data);
console.log('Comments:', comments.data);

// Wait for first completed request
const response = await Promise.race([
  http.get('/api/fast-endpoint'),
  http.get('/api/slow-endpoint')
]);

12. Retry Logic

async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await http.get(url);

    if (response.ok) {
      return response;
    }

    // Retry on server errors
    if (response.status >= 500 && i < maxRetries - 1) {
      console.log(`Retry ${i + 1}/${maxRetries}...`);
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
      continue;
    }

    return response;
  }
}

const response = await fetchWithRetry('/api/unstable-endpoint');

13. Progress Tracking (Upload)

// Use XMLHttpRequest for progress tracking
function uploadWithProgress(url, file, onProgress) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append('file', file);

    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const percent = (e.loaded / e.total) * 100;
        onProgress(percent);
      }
    });

    xhr.addEventListener('load', () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(xhr.statusText));
      }
    });

    xhr.addEventListener('error', () => reject(new Error('Upload failed')));

    xhr.open('POST', url);
    xhr.send(formData);
  });
}

// Usage
const file = document.querySelector('#file').files[0];
const result = await uploadWithProgress('/api/upload', file, (percent) => {
  console.log(`Uploading: ${percent.toFixed(2)}%`);
});

API Reference

HttpClient Class

Constructor

new HttpClient(options?: HttpClientOptions): HttpClient

Properties

Property Type Description
baseURL string Base URL for requests
defaultHeaders object Default headers
timeout number Timeout (ms)
throwOnError boolean Throw error on non-2xx
csrfToken string \| null CSRF token
interceptors object Request/Response interceptors

Methods

HTTP Methods
get(url: string, options?: RequestOptions): Promise<Response>
post(url: string, data?: any, options?: RequestOptions): Promise<Response>
put(url: string, data?: any, options?: RequestOptions): Promise<Response>
delete(url: string, options?: RequestOptions): Promise<Response>
patch(url: string, data?: any, options?: RequestOptions): Promise<Response>
head(url: string, options?: RequestOptions): Promise<Response>
options(url: string, options?: RequestOptions): Promise<Response>
Safe Methods
getSafe(url: string, options?: RequestOptions): Promise<Response>
postSafe(url: string, data?: any, options?: RequestOptions): Promise<Response>
putSafe(url: string, data?: any, options?: RequestOptions): Promise<Response>
deleteSafe(url: string, options?: RequestOptions): Promise<Response>
Other Methods
upload(url: string, files: File | File[], options?: UploadOptions): Promise<Response>
request(url: string, options?: RequestOptions): Promise<Response>
Configuration Methods
setBaseURL(url: string): void
setDefaultHeader(name: string, value: string): void
setDefaultHeaders(headers: object): void
setTimeout(timeout: number): void
setCsrfToken(token: string): void
Interceptor Methods
addRequestInterceptor(fn: (config) => config | Promise<config>): () => void
addResponseInterceptor(onFulfilled?: (response) => response, onRejected?: (error) => error): () => void

simpleFetch Utility

Methods

// HTTP Methods
fetch(url: string, options?: RequestOptions): Promise<Response>
get(url: string, options?: RequestOptions): Promise<Response>
post(url: string, data?: any, options?: RequestOptions): Promise<Response>
put(url: string, data?: any, options?: RequestOptions): Promise<Response>
delete(url: string, options?: RequestOptions): Promise<Response>
patch(url: string, data?: any, options?: RequestOptions): Promise<Response>

// Convenience Methods
json(url: string, options?: RequestOptions): Promise<any>
text(url: string, options?: RequestOptions): Promise<string>
postJson(url: string, data: any, options?: RequestOptions): Promise<any>

// Upload Method
upload(url: string, files: File | File[] | FormData, data?: object, options?: RequestOptions): Promise<Response>

// Configuration Methods
setBaseURL(url: string): simpleFetch
setHeaders(headers: object): simpleFetch
setHeader(name: string, value: string): simpleFetch
setTimeout(timeout: number): simpleFetch
removeHeader(name: string): simpleFetch
resetConfig(): simpleFetch

Best Practices

1. ✅ Choose the Right Export

// ❌ Bad: Use HttpClient class when unnecessary
const client = new HttpClient();
const response = await client.get('/api/users');

// ✅ Good: Use simpleFetch or http instance
const users = await simpleFetch.json('/api/users');
// or
const response = await http.get('/api/users');

2. ✅ Set Base URL

// ❌ Bad: Full URL every time
await http.get('https://api.example.com/users');
await http.get('https://api.example.com/posts');

// ✅ Good: Set base URL
http.setBaseURL('https://api.example.com');
await http.get('/users');
await http.get('/posts');

3. ✅ Use Interceptors for Common Tasks

// ❌ Bad: Add token every time
const token = localStorage.getItem('token');
await http.get('/api/users', {
  headers: { 'Authorization': `Bearer ${token}` }
});

// ✅ Good: Use interceptor
http.addRequestInterceptor(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  return config;
});

await http.get('/api/users');

4. ✅ Handle Errors Appropriately

// ❌ Bad: Don't check response
const response = await http.get('/api/users');
console.log(response.data); // may be null

// ✅ Good: Check response.ok
const response = await http.get('/api/users');
if (response.ok) {
  console.log(response.data);
} else {
  console.error('Error:', response.statusText);
}

// ✅ Better: Use httpThrow
try {
  const response = await httpThrow.get('/api/users');
  console.log(response.data);
} catch (error) {
  console.error('Error:', error.message);
}

5. ✅ Use AbortController for Long Requests

// ✅ Good: Can cancel
const controller = new AbortController();

button.addEventListener('click', () => {
  http.get('/api/large-data', {
    signal: controller.signal
  });
});

cancelButton.addEventListener('click', () => {
  controller.abort();
});

6. ✅ Specify Correct Content-Type

// ❌ Bad: Specify Content-Type for FormData
await http.post('/api/upload', formData, {
  headers: { 'Content-Type': 'multipart/form-data' }
});

// ✅ Good: Let browser set it
await http.post('/api/upload', formData);

7. ✅ Enable CSRF Protection

// ✅ Good: Enable CSRF protection
const client = new HttpClient({
  security: {
    csrf: {
      enabled: true,
      headerName: 'X-CSRF-Token'
    }
  }
});

8. ✅ Set Appropriate Timeout

// ❌ Bad: Same timeout for all requests
http.setTimeout(5000);

// ✅ Good: Different timeout by request type
http.setTimeout(30000); // Default

await http.get('/api/quick', { timeout: 5000 });
await http.post('/api/process', data, { timeout: 120000 });

9. ✅ Cleanup Interceptors

// ❌ Bad: Don't cleanup
http.addRequestInterceptor(config => {
  // ...
  return config;
});

// ✅ Good: Keep remove function
const removeInterceptor = http.addRequestInterceptor(config => {
  // ...
  return config;
});

// Cleanup when not needed
removeInterceptor();

10. ✅ Handle Network Errors

const response = await http.get('/api/users');

if (!response.ok) {
  if (response.status === 0 || response.status === 408) {
    // Network error or timeout
    alert('Cannot connect to server');
  } else if (response.status >= 500) {
    // Server error
    alert('Server error. Please try again later.');
  } else {
    // Other errors
    alert('An error occurred: ' + response.statusText);
  }
}

Summary

Choose the Right Export

If you need... Use...
Simplicity, lightweight simpleFetch
Safe mode (doesn't throw error) http
Automatic error throwing httpThrow
Custom configuration new HttpClient()

Key Features

Feature HttpClient http httpThrow simpleFetch
Interceptors
CSRF Protection
Throw on Error As configured
Custom Handler
Auto Bearer Token
Convenience Methods