Zero Dependencies • 8.5 kB Min+Gzip • 100% TypeScript

The Easiest & Most Powerful
API Client for JS & TS.

Tired of reinventing token refresh loops, cache TTLs, and retry backoffs for fetch and axios? api-flow-client gives you seamless 401 mutex queuing, smart LRU caching, auto-retry with jitter, and ready-to-use React hooks in one cohesive, featherweight package.

npm install api-flow-client
api.ts — Universal Setup
TypeScript 5.0+
import { createApi } from 'api-flow-client'

// 1. Single powerful client instance
export const api = createApi({
  baseURL: 'https://api.example.com/v1',

  // Mutex auth refresh — queues concurrent 401s automatically
  auth: {
    getAccessToken: () => localStorage.getItem('token'),
    refresh: async (refreshToken) => {
      const res = await fetch('/auth/refresh', { method: 'POST' })
      const { token } = await res.json()
      return token
    }
  },

  // In-memory LRU cache + TTL expiration
  cache: { enabled: true, ttl: 300_000 },

  // Exponential retry with randomized jitter
  retry: { attempts: 3, jitter: true }
})

// 2. Fully typed & deduped requests
const users = await api.get<User[]>('/users')
console.log(users.data, users.duration)
0
External Dependencies
Native Fetch with zero bloat
8.5 kB
Minified + Gzipped
Featherweight bundle impact
100%
Type-Safe TypeScript
Full generics for requests & data
94/94
Passing Unit Tests
Battle-tested rock solid reliability

Built for Modern Apps. No Extra Libraries Needed.

Say goodbye to pasting custom 401 interceptors, retry loops, and cache maps into every new project.

Auth & Token Refresh Mutex

When 5 requests hit a 401 expired token at once, api-flow halts them, executes exactly one refresh call, and replays all 5 requests automatically with the new token. Zero race conditions.

No Thundering Herd

Smart Memory LRU Cache

Zero-config in-memory caching with TTL expiration, in-flight request deduplication, and regex pattern invalidation via api.invalidateCachePattern().

0ms Cache Hits

Auto-Retry with Jitter

Configurable exponential backoff retries for network drops, timeouts, 429, and 5xx errors. Full jitter randomizes delays to protect backend services from retry spikes.

Reliable Networking

React Hooks Out of the Box

Import from api-flow-client/hooks: useGet, usePost, usePut, usePatch, useDelete, and useMutation with auto-cancellation on component unmount.

Tree-Shakeable Hooks

Next.js & SSR First-Class

Use createServerSideApi and extractServerHeaders in Next.js Server Components, App Router, Pages router, Nuxt, and Express with seamless cookie forwarding.

App Router Ready

Pluggable Adapters

Defaults to native zero-dependency fetch. Already have an existing Axios project? Just pass adapter: 'axios' to reuse your established Axios stack without rewrite.

Fetch + Axios

File Upload & Download Progress

Native progress event tracking with percentage loaded and byte counters via api.upload() and api.download().

Progress Events

Async Pagination Iterators

Effortlessly paginate datasets with for await (const page of api.paginate('/items')) supporting page-based and cursor-based APIs out of the box.

Async Iterators

See api-flow-client in Action

Click the buttons below to simulate realistic network scenarios and inspect how api-flow handles them.

live-debugger.log

Clean, Expressive, and Fully Typed

Explore ready-to-paste snippets for common production patterns.

import { createApi } from 'api-flow-client'

interface User {
  id: number
  name: string
  email: string
}

const api = createApi({
  baseURL: 'https://api.example.com',
  timeout: 10_000
})

// GET with generics
const res = await api.get<User[]>('/users')
console.log(res.data[0].name) // Type-safe User

// Full HTTP suite
await api.post('/users', { name: 'Alex', email: 'alex@dev.com' })
await api.put('/users/1', { name: 'Alex Updated' })
await api.patch('/users/1', { email: 'new@dev.com' })
await api.delete('/users/1')
await api.head('/users/1')
await api.options('/users')
import { createApi } from 'api-flow-client'

const api = createApi({
  baseURL: 'https://api.example.com',
  auth: {
    // Injects "Authorization: Bearer <token>" automatically
    getAccessToken: () => localStorage.getItem('access_token'),
    getRefreshToken: () => localStorage.getItem('refresh_token'),
    
    // Called ONCE when any request receives 401
    refresh: async (refreshToken) => {
      const response = await fetch('https://api.example.com/auth/refresh', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refreshToken })
      })
      const data = await response.json()
      localStorage.setItem('access_token', data.accessToken)
      return data.accessToken
    }
  }
})
import { createApi } from 'api-flow-client'

const api = createApi({
  baseURL: 'https://api.example.com',
  cache: {
    enabled: true,
    ttl: 5 * 60 * 1000, // 5 minutes TTL
    maxSize: 150         // LRU limit
  }
})

// 1. First call fetches over network
const u1 = await api.get('/users')

// 2. Second call returns from memory in 0ms
const u2 = await api.get('/users')

// 3. Invalidate specific URL
api.clearCache('/users')

// 4. Regex pattern bulk invalidation (NEW in v1.1.0)
api.invalidateCachePattern(/\/users\//)
import React from 'react'
import { api } from './api'
import { useGet, usePost, useDelete } from 'api-flow-client/hooks'

export function UserProfile() {
  // Auto-fetches on mount, cancels on unmount
  const { data, loading, error, refetch } = useGet<User>(api, '/profile')

  // Mutation hooks for updates
  const { mutate: updateProfile, loading: saving } = usePost(api, '/profile')
  const { mutate: deleteAccount } = useDelete(api, '/profile')

  if (loading) return <div>Loading user profile...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <div>
      <h1>{data?.name}</h1>
      <button onClick={() => updateProfile({ name: 'New Name' })}>Update</button>
      <button onClick={() => refetch()}>Refresh</button>
    </div>
  )
}
// app/users/page.tsx (Next.js App Router / SSR)
import { createServerSideApi, extractServerHeaders } from 'api-flow-client'
import { headers } from 'next/headers'

export default async function UsersPage() {
  const incomingHeaders = await headers()
  
  // Forwards auth cookies & IP transparently
  const api = createServerSideApi({
    baseURL: process.env.INTERNAL_API_URL,
    headers: extractServerHeaders({ headers: incomingHeaders })
  })

  const response = await api.get<User[]>('/users')

  return (
    <main>
      <h1>Server-Rendered Users ({response.data.length})</h1>
    </main>
  )
}
import { api } from './api'

// 1. Page-based Async Iterator
for await (const page of api.paginate<User>('/users', { limit: 25 })) {
  console.log(`Page ${page.page}:`, page.data)
  if (!page.hasMore) break
}

// 2. Cursor-based Async Iterator
for await (const batch of api.cursorPaginate<LogEntry>('/logs', { limit: 100 })) {
  processLogs(batch.data)
}

How Does api-flow-client Compare?

See how it stacks up against standard fetch, Axios, and Ky.

Feature api-flow-client Axios Native Fetch Ky
Bundle Size (min+gzip) 8.5 kB ~13.5 kB 0 kB (built-in) ~6.2 kB
External Dependencies 0 1 (follow-redirects) 0 0
401 Token Refresh Mutex ✓ Built-In ✗ Manual Boilerplate ✗ None ✗ None
In-Memory LRU Cache + TTL ✓ Built-In ✗ Requires Plugin ✗ None ✗ None
Regex Pattern Invalidation ✓ Built-In ✗ None ✗ None ✗ None
Auto-Retry with Jitter ✓ Built-In ✗ Requires Plugin ✗ None ✓ Built-In
In-Flight Request Deduplication ✓ Built-In ✗ None ✗ None ✗ None
Official React Hooks Package ✓ Built-In (useGet, usePost...) ✗ Third Party ✗ None ✗ None
SSR Cookie & Header Extraction ✓ Built-In ✗ Manual ✗ Manual ✗ Manual
File Upload / Download Progress ✓ Built-In ✓ Built-In ✗ Complex Streams ✗ None

Common Questions

Is api-flow-client truly zero dependencies?

Yes! In default mode, api-flow-client has zero runtime dependencies. It uses the platform's native fetch and AbortController, which are standard in modern browsers, Node.js 18+, Bun, Deno, and React Native. Axios is only an optional peer dependency if you explicitly want to use the Axios adapter.

How does the 401 token refresh mutex work?

When multiple concurrent API calls receive a 401 Unauthorized status, standard clients fire multiple simultaneous refresh token requests (which can cause invalidation race conditions). api-flow-client utilizes an internal mutex queue: the first request triggers your auth.refresh() callback while subsequent failing requests are parked in a promise queue. Once the new token arrives, all queued requests are automatically re-executed with the new authorization header.

Can I use api-flow-client in Next.js Server Components?

Absolutely. api-flow-client exports createServerSideApi and extractServerHeaders, specifically designed for Next.js App Router (Server Components & Server Actions) and Pages Router. It automatically extracts incoming cookies, client IPs, and auth tokens from headers and forwards them without browser-only APIs.

How do I install the React hooks?

The React hooks are bundled inside the same package under a dedicated export path: import { useGet, useMutation, usePost, usePut, usePatch, useDelete } from 'api-flow-client/hooks'. This keeps them tree-shaken from non-React environments like pure Node.js services or Vue applications.

How can I contribute or report a bug?

Visit the official GitHub repository at github.com/imtiaz-dev-0/api-flow. Open an issue or pull request. Contributions, bug reports, and feedback are always welcome!