⚡ Z-Fetch

🤝 All Options

Complete guide to instance configuration options and request-level settings.

Z-Fetch provides comprehensive configuration options that can be set at the instance level for consistent behavior across all requests, or at the request level for specific customization.

Instance Configuration

Set default options when creating an instance:

import { createInstance } from "@z-fetch/fetch";
 
const api = createInstance({
  baseUrl: "https://api.example.com",
  timeout: 30000,
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
  },
  withCredentials: true,
  retries: 3,
});

Core Configuration Options

Base URL

Set a base URL for all requests:

const api = createInstance({
  baseUrl: "https://api.example.com",
});
 
// Requests will be made to: https://api.example.com/users
await api.get("/users");
await api.post("/users", { body: userData });

Headers

Set default headers for all requests:

const api = createInstance({
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
    "User-Agent": "MyApp/1.0",
    "X-API-Version": "v2",
  },
});

Headers are intelligently merged:

// Instance headers + request headers
await api.post("/users", {
  headers: {
    Authorization: "Bearer token",
    "X-Request-ID": "abc123",
  },
  body: userData,
});
// Final headers include both instance and request headers

Authentication

Bearer Token

Automatic Bearer token authentication:

const api = createInstance({
  bearerToken: "your-jwt-token",
});
 
// Automatically adds: Authorization: Bearer your-jwt-token
await api.get("/protected-endpoint");

Basic Authentication

const api = createInstance({
  headers: {
    Authorization: `Basic ${btoa("username:password")}`,
  },
});

API Key Authentication

const api = createInstance({
  headers: {
    "X-API-Key": "your-api-key",
  },
});

Credentials and CORS

Control how credentials are sent with requests:

const api = createInstance({
  withCredentials: true, // Sets fetch credentials: 'include'
  mode: "cors",
  headers: {
    "Access-Control-Allow-Origin": "*",
  },
});
  • Fetch path: sets credentials: 'include'.
  • XHR path (when using progress callbacks): sets xhr.withCredentials = true.

Advanced Configuration

Timeout

Set request timeout in milliseconds:

const api = createInstance({
  timeout: 30000, // 30 seconds
});
 
// Per-request timeout override
await api.get("/slow-endpoint", {
  timeout: 60000, // 60 seconds for this request
});

Retry Configuration

Configure automatic retries for failed requests:

const api = createInstance({
  retries: 3, // Number of retries
  retryDelay: 1000, // Delay between retries (ms)
  retryCondition: (error) => {
    // Custom retry logic
    return error.status >= 500 || error.message.includes("network");
  },
});

Cache Configuration

Control request and response caching:

const api = createInstance({
  cache: "no-cache", // native fetch cache control
  withCache: true, // Enable Z-Fetch caching for GET
  revalidateCache: 60000, // Revalidate cache every minute
});
  • Successful GET responses are cached when withCache: true.
  • Failed or canceled GETs are not cached; the next call hits the network.
  • Background revalidation preserves existing cache on failure.

Progress Tracking Configuration

Upload Progress

const api = createInstance({
  onUploadProgress: (event) => {
    const percentage = Math.round((event.loaded / event.total) * 100);
    console.log(`Upload: ${percentage}%`);
    updateGlobalUploadProgress(percentage);
  },
});

Download Progress

const api = createInstance({
  onDownloadProgress: (event) => {
    const percentage = Math.round((event.loaded / event.total) * 100);
    console.log(`Download: ${percentage}%`);
    updateGlobalDownloadProgress(percentage);
  },
});

Error Configuration

Error Mapping

Map error codes and messages to user-friendly text:

const api = createInstance({
  errorMapping: {
    401: "Please sign in to continue",
    403: "You do not have permission for this action",
    404: "The requested resource was not found",
    429: "Too many requests. Please slow down.",
    500: "Internal server error. Please try again.",
    "fetch failed": "Network connection failed",
    timeout: "Request timed out",
  },
});

Error Handling

const api = createInstance({
  hooks: {
    onError: (context) => {
      // Global error handling
      console.error("API Error:", context.error);
      trackError(context.error);
    },
  },
});

Request/Response Processing

JSON Handling

Control automatic JSON parsing:

const api = createInstance({
  parseJson: true, // Auto-parse JSON responses
  stringifyPayload: true, // Auto-stringify request bodies
});
 
// Disable for specific requests
await api.post("/upload", {
  body: formData,
  parseJson: false,
  stringifyPayload: false,
});

Response Processing

const api = createInstance({
  hooks: {
    onResponse: (context) => {
      // Global response processing
      console.log("Response received:", context.response.status);
 
      // Add response metadata
      return {
        data: {
          ...context.data,
          receivedAt: Date.now(),
        },
      };
    },
  },
});

Environment-Based Configuration

Development vs Production

const api = createInstance({
  baseUrl:
    process.env.NODE_ENV === "production"
      ? "https://api.example.com"
      : "https://dev-api.example.com",
 
  timeout:
    process.env.NODE_ENV === "development"
      ? 60000 // Longer timeout in dev
      : 30000,
 
  headers: {
    "X-Environment": process.env.NODE_ENV,
    ...(process.env.NODE_ENV === "development" && {
      "X-Debug": "true",
    }),
  },
});

Configuration Loading

// Load from environment variables
const api = createInstance({
  baseUrl: process.env.REACT_APP_API_URL,
  timeout: parseInt(process.env.REACT_APP_API_TIMEOUT) || 30000,
  bearerToken: process.env.REACT_APP_API_TOKEN,
  headers: {
    "X-API-Key": process.env.REACT_APP_API_KEY,
  },
});

Complete Configuration Example

import { createInstance } from "@z-fetch/fetch";
 
const api = createInstance({
  // Base configuration
  baseUrl: "https://api.example.com",
  timeout: 30000,
 
  // Authentication
  bearerToken: localStorage.getItem("authToken"),
 
  // Headers
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
    "X-API-Version": "v2",
    "X-Client-Version": "1.0.0",
  },
 
  // CORS and credentials
  withCredentials: true,
  mode: "cors",
 
  // Error handling
  errorMapping: {
    401: "Authentication required",
    403: "Access denied",
    404: "Resource not found",
    429: "Rate limit exceeded",
    500: "Server error",
    "fetch failed": "Network error",
  },
 
  // Progress tracking
  onUploadProgress: (event) => {
    const percentage = Math.round((event.loaded / event.total) * 100);
    updateUploadProgress(percentage);
  },
 
  onDownloadProgress: (event) => {
    const percentage = Math.round((event.loaded / event.total) * 100);
    updateDownloadProgress(percentage);
  },
 
  // Retry configuration
  retries: 3,
  retryDelay: 1000,
 
  // Cache configuration
  withCache: true,
 
  // Hooks
  hooks: {
    onRequest: (context) => {
      // Add request timestamp
      context.setHeaders((headers) => ({
        ...headers,
        "X-Request-Time": Date.now().toString(),
      }));
 
      // Log requests in development
      if (process.env.NODE_ENV === "development") {
        console.log(`→ ${context.request.method} ${context.request.url}`);
      }
    },
 
    onResponse: (context) => {
      // Log responses in development
      if (process.env.NODE_ENV === "development") {
        console.log(`← ${context.response.status} ${context.request.url}`);
      }
 
      // Add response metadata
      return {
        data: {
          ...context.data,
          meta: {
            requestTime: context.request.headers["X-Request-Time"],
            responseTime: Date.now(),
            status: context.response.status,
          },
        },
      };
    },
 
    onError: (context) => {
      // Log errors
      console.error("API Error:", {
        url: context.request?.url,
        method: context.request?.method,
        status: context.error?.status,
        message: context.error?.message,
      });
 
      // Track errors
      if (typeof gtag !== "undefined") {
        gtag("event", "api_error", {
          error_status: context.error?.status,
          error_url: context.request?.url,
        });
      }
 
      // Handle authentication errors
      if (context.error?.status === 401) {
        localStorage.removeItem("authToken");
        window.location.href = "/login";
      }
    },
  },
});
 
export default api;

Request-Level Overrides

Any instance configuration can be overridden at the request level:

// Override instance settings for specific request
await api.post("/special-endpoint", {
  timeout: 60000, // Override instance timeout
  headers: {
    "X-Special": "true", // Additional headers
  },
  bearerToken: specialToken, // Override instance token
  withCredentials: false, // Override credentials setting
  body: requestData,
});

Configuration Priority

Request-level options always take precedence over instance-level options.