Skip to content

devraj-labs/api-kit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Devraj Labs · API Kit

The API client that handles auth so you don't have to.

Token attachment. Silent refresh. Race-condition safety. Works everywhere.

npm TypeScript License: MIT Platform


You've written the interceptor. You've debugged the race condition where five requests all tried to refresh at once. You've added the retry logic, the logger, the storage adapter. You've done it on every project.

API Kit does all of it — once, correctly.


What it does

ApiClient.getInstance({
  baseURL: "https://api.example.com",
  storage: myStorage,
  hooks: {
    onAuthFailure: () => router.push("/login"),
  },
});

// That's it. Every request from here automatically:
// - attaches Bearer token
// - refreshes silently when it expires
// - retries if the server hiccups
// - routes exactly ONE refresh call even if 10 requests fire at once
const { data } = await client.get<User>("/api/me");

Features

Token lifecycle — fully automatic

  • Attaches Authorization: Bearer <token> to every request
  • Detects 401s and transparently refreshes + replays the failed request
  • Pre-emptive refresh — renews the token before it expires, configurable via preemptiveRefreshSeconds

Race-condition safe

  • Concurrent 401s collapse into exactly one refresh call
  • All queued requests drain together the moment refresh completes
  • Pre-emptive refresh shares the same single-flight promise — no double-refresh under load

Platform agnostic

  • Works in browser, React Native, and Node.js
  • Bring your own storage — any object implementing getAccessToken / setTokens / clearTokens
  • Ships with MemoryTokenStorage, LocalStorageTokenStorage, and AsyncStorageTokenStorage
  • First-class support for @devraj-labs/rn-storage-kit (encrypted keychain on mobile)

Production grade

  • Exponential backoff with full jitter on transient errors (408, 429, 5xx)
  • Auth event hooks — onTokenRefreshed · onAuthFailure · onLogout
  • Bring-your-own logger — any { debug, info, warn, error } object (console, @devraj-labs/logger, etc.)
  • Singleton pattern — initialise once, use everywhere, no prop drilling

TypeScript first

  • Full generics on every method: client.get<User>("/api/me")
  • Exported interfaces for every extension point

Install

npm install @devraj-labs/api-kit axios

Quick start

// bootstrap.ts — run this once at app entry (index.ts / App.tsx)
import { ApiClient, LocalStorageTokenStorage } from "@devraj-labs/api-kit";

ApiClient.getInstance({
  baseURL: "https://api.example.com",
  storage: new LocalStorageTokenStorage(),
  hooks: {
    onAuthFailure() {
      window.location.href = "/login";
    },
  },
});
// anywhere in your app — no config needed
import { ApiClient } from "@devraj-labs/api-kit";

const client = ApiClient.getInstance();

const { data: user }    = await client.get<User>("/api/me");
const { data: post }    = await client.post<Post>("/api/posts", { title });
const { data: updated } = await client.patch<Post>("/api/posts/1", { title });

After login, store the tokens and every subsequent request is handled:

import { ApiClient } from "@devraj-labs/api-kit";

async function login(username: string, password: string) {
  const res = await fetch("/auth/login", {
    method: "POST",
    body: JSON.stringify({ username, password }),
  });
  const tokens = await res.json();

  // hand tokens to api-kit — it takes it from here
  const client = ApiClient.getInstance();
  await client.storage.setTokens(tokens);
}

Storage

Adapter Environment Security
MemoryTokenStorage Any Cleared on page reload
LocalStorageTokenStorage Browser Vulnerable to XSS — dev/demo only
AsyncStorageTokenStorage React Native Unencrypted — dev/demo only
@devraj-labs/rn-storage-kit React Native Encrypted keychain — recommended for prod

Implement your own with three methods:

import type { TokenStorage } from "@devraj-labs/api-kit";

const myStorage: TokenStorage = {
  getAccessToken:  () => secureGet("access_token"),
  getRefreshToken: () => secureGet("refresh_token"),
  setTokens:       (t) => secureSet(t),
  clearTokens:     ()  => secureClear(),
};

Full guide → docs/storage.md


Logger

Pass any object with debug / info / warn / error — no adapter needed:

// console
ApiClient.getInstance({ ..., logger: console });

// @devraj-labs/logger
import { logger } from "@devraj-labs/logger";
ApiClient.getInstance({ ..., logger: logger.child({ module: "api-kit" }) });

// silent (default)
ApiClient.getInstance({ ...config }); // omit logger entirely

Full guide → docs/logger.md


Documentation

Getting Started Install, minimal setup, platform examples
Configuration All config options, types, and defaults
Token Storage Built-in adapters, custom storage, rn-storage-kit
Logger Integration Using @devraj-labs/logger or any compatible logger
Race Condition Guard How concurrent 401s are handled
Pre-emptive Refresh Proactive token renewal before expiry
Examples Node.js · Browser · React Native · custom storage
Demo Running the interactive demo app locally

Run the demo

Hosted demo — no setup needed: https://web-self-delta-77.vercel.app/

See token refresh, race-condition guard, and live expiry countdown in action. Login with alice / password123.

Or run it locally:

npm run demo

Opens a Vite + React app at http://localhost:5173 backed by a local Express server. Full guide → docs/demo.md


License

MIT © Devraj Labs

About

Production-grade API client with automatic token lifecycle management, race-condition-safe refresh handling, and platform-agnostic storage adapters.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors