npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fetchflow

v1.0.3

Published

A production-grade React data fetching library with caching, retry, error handling, REST & GraphQL support.

Readme

FetchFlow

Purpose

FetchFlow A modern, production-ready data fetching library for React. FetchFlow helps you fetch, cache, and manage data from REST or GraphQL APIs with minimal code, strong TypeScript support, robust error handling, retries, and performance-focused caching. It unifies REST and GraphQL requests with built-in caching, retries, and robust error handling. Designed for modern apps—no boilerplate, just reliable data, fast.

Why FetchFlow?

  • No more boilerplate: Unified hooks for REST and GraphQL, so you never re-write the same fetch/useEffect code again.
  • Automatic caching & retries: Avoid unnecessary network calls, handle flaky networks gracefully.
  • Consistent error handling: Standardized error objects, so you never need to guess why a request failed.
  • Framework focused: Built for React (Vue support can be requested via issues).

Key Features

  • Works with REST and GraphQL out of the box.
  • Type-safe React hooks: useFetch, usePoll, useMutation
  • Built-in cache (memory & localStorage)
  • Retry with backoff for transient errors
  • Clean error messages via a single FetchError class
  • Easy to extend and contribute

Installation

npm install fetchflow

Quick Usage

1. Simple REST Fetch

import { useFetch } from 'fetchflow';

const { data, isLoading, error, refetch } = useFetch<User[]>(
  'https://jsonplaceholder.typicode.com/users',
);

2. POST/PUT/DELETE with useMutation

import { useMutation } from 'fetchflow';

const { mutate, isMutating, error } = useMutation<User, Partial<User>>({
  url: 'https://api.example.com/users',
  method: 'POST',
  onSuccess: (created) => alert('User created: ' + created.id),
});

3. Polling Data (auto refresh)

import { usePoll } from 'fetchflow';

const { data } = usePoll<User[]>('https://api.example.com/users', { intervalMs: 10000 });

GraphQL Example

import { graphqlClient } from 'fetchflow';

const query = `
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
    }
  }
`;

const data = await graphqlClient<{ user: User }>('https://graphql.example.com', {
  query,
  variables: { id: 1 },
});

SSR Compatibility

FetchFlow works seamlessly in both browser and Node.js. Under Node 18+ you have a global fetch, but you can still inject a custom implementation (e.g. to add logging, metrics, or polyfills) via the customFetch option. No browser‐only APIs are used in core.

// pages/index.tsx (Next.js example)

import React from 'react';
import fetch from 'node-fetch';                // or leave out in Node 18+
import { useFetch } from 'fetchflow';

type Item = { id: number; name: string };

export default function Page() {
  // On the server, Next.js will render this component,
  // useFetch will use the global fetch or your customFetch.
  const { data, isLoading, error } = useFetch<Item[]>(
    'https://api.example.com/items',
    {
      // only needed if you want to override or polyfill fetch:
      customFetch: fetch,
      cacheTimeMs: 2 * 60_000,  // 2-minute cache
    }
  );

  if (isLoading) return <p>Loading…</p>;
  if (error)     return <p>Error: {error.message}</p>;
  return (
    <ul>
      {data!.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}
  • No extra setup: In modern Node 18+ environments you can omit customFetch entirely and it will default to the built-in fetch.
  • Safe in SSR: No references to window or browser-only APIs leak into your server bundle.
  • Flexible: Swap in any fetch-compatible function for logging, telemetry, or testing.

Advanced: Authenticated/API Credential Requests

REST (token in header)

import { useFetch } from 'fetchflow';

const { data } = useFetch<User[]>('https://api.example.com/secure/users', {
  headers: {
    Authorization: `Bearer ${yourAuthToken}`,
  },
});

GraphQL (with credentials)

import { graphqlClient } from 'fetchflow';

const result = await graphqlClient<{ user: User }>(
  'https://graphql.example.com',
  { query, variables: { id: 123 } },
  {
    headers: {
      Authorization: `Bearer ${yourAuthToken}`,
    },
    credentials: 'include', // if you want to send cookies as well
  },
);

List of Main Methods / Hooks

| Method | Description | | --------------- | ------------------------------------------- | | useFetch | Fetch & cache REST or GraphQL data (GET). | | usePoll | Auto-refresh/poll data at intervals. | | useMutation | POST/PUT/PATCH/DELETE data (write/update). | | graphqlClient | Use for advanced GraphQL queries/mutations. | | FetchError | Standardized error handling for all hooks. |

How does FetchFlow handle caching?

By default, FetchFlow uses an in-memory cache for all fetches. This means repeated fetches to the same URL (with the same params) within a cache window will return instantly from memory (no network call).

Default Behavior

  • All hooks (useFetch, usePoll, etc.) use the in-memory cache automatically.
  • Cache expiry (TTL) can be configured with the cacheTimeMs option (default: 5 minutes).

How to Use the Memory Cache

You don’t have to do anything extra—it “just works” out of the box!

const { data } = useFetch<User[]>('https://api.com/users');
// Second call with same params will return cached value until expired.

To change cache duration:

const { data } = useFetch<User[]>('https://api.com/users', {
  cacheTimeMs: 60000, // 1 minute cache
});

How to Use LocalStorage (Persistent) Cache

You can manually instantiate and use the StorageCache class provided and wire it into your own data fetching logic, e.g., by replacing the in-memory cache in your custom hook.

Example: Using StorageCache directly

import { StorageCache } from 'fetchflow';

// Create a persistent cache instance
const persistentCache = new StorageCache<User[]>();

// Set value (cache for 10 minutes)
persistentCache.set('my-key', [{ id: 1, name: 'Alice' }], 600_000);

// Get value (auto-removes if expired)
const users = persistentCache.get('my-key');

Note

  • The core useFetch and usePoll hooks use only in-memory cache by default for performance and simplicity.
  • If you want to swap in localStorage-based cache globally, you would need to fork and slightly extend the hook to allow a cacheProvider option.

Login Request: How to Retrieve & Store a Session Token

import React, { useState } from 'react';
import { useMutation } from 'fetchflow';

type LoginResponse = { token: string };
type LoginPayload = { username: string; password: string };

export function LoginForm() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [sessionToken, setSessionToken] = useState<string | null>(null);

  const { mutate, isMutating, error } = useMutation<LoginResponse, LoginPayload>({
    url: 'https://api.example.com/login',
    method: 'POST',
    onSuccess: (response) => {
      // Save the token in localStorage (or cookies)
      localStorage.setItem('sessionToken', response.token);
      setSessionToken(response.token);
    },
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    mutate({ username, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        placeholder="Username"
        value={username}
        onChange={e => setUsername(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={e => setPassword(e.target.value)}
      />
      <button type="submit" disabled={isMutating}>
        {isMutating ? 'Logging in...' : 'Login'}
      </button>

      {sessionToken && <div>Logged in! Token: {sessionToken}</div>}

      {error && <div style={{ color: 'red' }}>Login failed: {error.message}</div>}
    </form>
  );
}

How to Handle Login Error

The error property from useMutation will be populated with a FetchError if the credentials are wrong or if there’s a network/server issue. You can display error.message to the user.

{error && <div style={{ color: 'red' }}>Login failed: {error.message}</div>}

How to Use the Token in Future Requests

After storing the token, use it in your API calls:

import { useFetch } from 'fetchflow';

const token = localStorage.getItem('sessionToken');
const { data, error } = useFetch<User[]>('https://api.example.com/users', {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

If You Prefer Cookies

If the backend sets an HTTP-only cookie (via Set-Cookie), you might need to set credentials: 'include' in your hook options:

const { mutate } = useMutation<LoginResponse, LoginPayload>({
  url: 'https://api.example.com/login',
  method: 'POST',
  credentials: 'include', // Send/receive cookies
  // ...
});

And in fetches:

const { data } = useFetch<User[]>('https://api.example.com/users', { credentials: 'include' });

FetchFlow gives you full control to handle authentication in a type-safe, robust, and standard way!

Contributing

Contributions are welcome! Please:

  • Fork the repository.
  • Create a branch for your changes.
  • Write tests for any new features.
  • Submit a pull request with detailed changes.

License

This project is licensed under the MIT License