Skip to content

z-fetch/z-fetch

Repository files navigation

⚑ Z-Fetch

The pragmatic native fetch API wrapper for JavaScript.

Just arguments and wraps native fetch so can work in any framework or JavaScript environment where fetch is available with no additional setup needed.

πŸ‘‰ See Full Documentation

πŸš€ Features

  • Framework Agnostic – use in any JavaScript project.
  • Zero dependencies, just wraps the native fetch API.
  • Intuitive modern API with per-request configuration and flexibility.
  • Supports request cancellation on demand or on timeout.
  • Global configuration for common request options.
  • Supports all native fetch API options.
  • Built-in caching with auto revalidation.
  • Built-in request retries and configurable polling.
  • Built-in helpers for common use cases such as setting bearer token!
  • Auto JSON parsing and graceful error handling.
  • Builtin hooks or interceptors.
  • Request refetching on demand.
  • New! Additional HTTP methods: OPTIONS, TRACE, HEAD and a CUSTOM method for any other HTTP verb.
  • New! TypeScript support for better type safety and autocompletion.
  • New! Test coverage for all core features and methods, for better reliability.

▢️ Installation

  1. Using npm, yarn or pnpm

    npm install @z-fetch/fetch

πŸ§‘β€πŸ’» How It Works?

Creating Custom Instances

Z-Fetch allows you to create custom instances with their own configuration, which is useful for different API endpoints or services in your application.

Basic Instance Creation - Recommended for most applications

import { createInstance } from "@z-fetch/fetch";

// Create a custom instance with specific configuration
const api = createInstance({
  baseUrl: "https://jsonplaceholder.typicode.com",
  headers: {
    "X-Custom-Header": "custom-value",
  },
  timeout: 30000,
});

// Use the instance to make requests
const getPosts = async () => {
  const result = await api.get("/posts");
  if (result?.data) {
    console.log("Posts:", result.data);
  }
};

const createPost = async () => {
  const result = await api.post("/posts", {
    body: {
      title: "New Post",
      body: "This is the content",
      userId: 1,
    },
  });

  if (result?.data) {
    console.log("Created post:", result.data);
  }
};

πŸ˜‡ Quick Use, No Instance Needed!

GET Request

import { GET } from "@z-fetch/fetch";

const getPosts = async () => {
  const { data, error } = await GET(
    "https://jsonplaceholder.typicode.com/posts",
  );
  if (data) {
    console.log("Data:", data);
  } else {
    console.error("Error:", error.message);
  }
};

POST Request

import { POST } from "@z-fetch/fetch";

const createPost = async () => {
  const { data, error } = await POST(
    "https://jsonplaceholder.typicode.com/posts",
    {
      body: {
        title: "dune",
        body: "a story about the dune verse!",
        userId: 1,
      },
    },
  );
  if (data) {
    console.log("Data:", data);
  } else {
    console.error("Error:", error.message);
  }
};

πŸ‘‰ Visit the docs for more examples on how to use and to explore full functionality.

Cancellation

  • Cancel early via the returned promise: const p = GET('/users'); p.cancel(); const r = await p;
  • error.status is "CANCELED" with message "Request canceled".
  • Timeouts set error.status to "TIMEOUT" with message "Request timed out!".
  • Instances also expose result.cancelRequest() on the resolved result for aborting ongoing work.
// Cancel a request
const p = api.get("/users", { timeout: 10000 });
setTimeout(() => p.cancel(), 50);
const res = await p;
if (res.error?.status === "CANCELED") {
  // handle cancel
}

Cache Behavior

  • Successful GETs are cached when withCache: true.
  • Failed or canceled GETs are NOT cached; subsequent calls hit the network.
  • Background revalidation runs after revalidateCache ms; on failure, cached data is preserved.
const api = createInstance({ withCache: true, revalidateCache: 1000 });
await api.get("/items"); // caches on success
const { data } = await api.get("/items"); // served from cache; revalidates in background

Credentials

  • withCredentials: true sets fetch credentials: 'include' and XHR withCredentials = true.
  • By default credentials are not included. You can still set native credentials per request when withCredentials is not used.

πŸ”§ Recent Fixes & Enhancements

Bearer Token Support (Fixed Issue #3)

Now you can pass bearerToken directly in request options:

import { GET, POST } from "@z-fetch/fetch";

// Pass bearer token in request options
const result = await GET("/api/protected", {
  bearerToken: "your-token-here",
});

// Works with all HTTP methods
const postResult = await POST("/api/data", {
  body: { title: "My Post" },
  bearerToken: "your-token-here",
});

Instance Configuration (Fixed Issue #4)

Instance options like withCredentials now work correctly:

import { createInstance } from "@z-fetch/fetch";

const api = createInstance({
  baseUrl: "https://api.example.com",
  withCredentials: true, // Now works properly!
  headers: {
    "Content-Type": "application/json",
  },
});

// All requests will include credentials
const result = await api.get("/user-data");

Error Handling

Z-Fetch provides a consistent { data, error } result for all requests. HTTP errors (4xx, 5xx), network errors, timeouts, and cancellations all populate the error object with a unified structure.

import { createInstance, GET } from "@z-fetch/fetch";

const api = createInstance({
  baseUrl: "https://api.example.com",
});

// Always destructure data and error
const { data, error } = await api.get("/protected");

if (error) {
  // Unified error structure for all error types
  console.log("Error:", error.message); // Error message
  console.log("Status:", error.status); // 401, 404, 500, "NETWORK_ERROR", "TIMEOUT", etc.
  console.log("Data:", data); // Response body still parsed even on error
} else {
  console.log("Success:", data);
}

Error Mapping

Customize error messages by status code or pattern using errorMapping:

const api = createInstance({
  baseUrl: "https://api.example.com",
  errorMapping: {
    // Map status codes to custom messages
    401: "Authentication failed - please sign in again",
    403: "Access denied - insufficient permissions",
    404: "Resource not found",
    500: "Server error - please try again later",
    // Map z-fetch internal errors
    NETWORK_ERROR: "Network connection failed",
    TIMEOUT: "Request took too long",
  },
});

const { data, error } = await api.get("/protected");
if (error) {
  console.log(error.message); // Uses custom mapped message
  console.log(error.status); // 401, 404, 500, etc.
}

Note: Error mapping is optional. When no mapping is provided for a status code, the library uses response.statusText as the default message.


⚠️ DEPRECATED (v0.0.14): The mapErrors and throwOnError configuration options have been removed. Z-Fetch now always creates error objects for HTTP errors and never throws. The API is unified - always use const { data, error } = result for all requests.

🌟 Contributing

That’s it for now! More features will surely come, but this version of Z-Fetch already elevates fetching in your applications with enhanced flexibility and control. Feel free to contribute, share suggestions, or propose new features. I’d be happy to hear from you!

Humble regards, I am Hussein Kizz

About

The pragmatic native fetch API wrapper for JavaScript.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors