A lightweight HTTP and operation wrapper built on Axios for React/Vite projects. Supports pluggable authentication strategies, global config, toast integration, request retry after refresh, and clean async operation management.
- ✅ Simple Axios wrapper with unified config
- ✅ Pluggable
AuthStrategy(cookie-based / token-based) - ✅ Built-in token storage (
memory/localStorage) - ✅ Automatic refresh + retry on expired access tokens
- ✅ Global
toastintegration (decoupled from UI) - ✅
operator()helper for async request + loading + error feedback - ✅ Fine-grained per-request toggles (
skipAuth,skipRefresh,skipUnauthorizedHandler) - ✅ React hook
useRequestfor automatic requests with cancellation - ✅ Helper functions
login/logoutfor auto token management - ✅ Minimal dependencies, framework agnostic (core) + optional React add-on
npm install @mints/request axios
axiosis a peer dependency — please install it in your project.
Call setupRequest() once before using request or operator (e.g. in src/setup.ts or main.tsx):
// setup.ts
import { setupRequest, createCookieStrategy } from '@mints/request';
import { toast } from '@mints/ui'; // your own toast system
setupRequest({
baseURL: '/api',
toast: {
success: toast.success,
error: toast.error,
},
auth: createCookieStrategy({
refreshPath: '/auth/refresh',
tokenField: 'jwt', // default: "access_token"
}),
onUnauthorized: () => {
window.location.href = '/login';
},
});- By default
createCookieStrategystorestokenin memory (memoryStorage). - You can pass a custom
storage(e.g.localStorageStorage) if persistence is needed. - Default refresh status codes:
401, 419, 440.
import { request } from '@mints/request';
// Defaults to request.public
const users = await request('/users');
// or
const users = await request.public('/users');
// Authenticated API
const me = await request.auth('/me');request.public(url, config)→ no auth, no refresh, safe for public endpoints.request.auth(url, config)→ includes auth, retries after refresh if needed.request.init(url, { soft?: boolean })→ probe request, optionalsoft=trueskips refresh.request.reset(url)→ reset probe state.
import { operator, request } from '@mints/request';
const [ok, data, err] = await operator(() =>
request.auth('/users', { params: { q: 'admin' } }),
);import { login, logout } from '@mints/request/auth';
await login(() => API.auth.login(form));
await logout(() => API.auth.logout());import { useRequest } from '@mints/request/react';
import { request } from '@mints/request';
function Example() {
const { loading, data, error } = useRequest(
(signal) => request.auth('/users', { signal }),
[], // deps
{ name: 'fallback' }, // optional initial value
);
if (loading) return <span>Loading...</span>;
if (error) return <span>Failed: {String(error)}</span>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Set global request behavior.
type GlobalRequestConfig = {
baseURL?: string;
defaultHeaders?: () => Record<string, string>;
toast?: {
success?: (msg: string) => void;
error?: (msg: string) => void;
};
onUnauthorized?: () => void;
// Authentication
auth?: AuthStrategy;
retryAfterRefresh?: number; // default 1
shouldRefreshOnStatus?: (status: number) => boolean; // default: 401, 419, 440
};// Callable
const data = await request('/path');
// With modes
await request.public('/path', { credentials: 'never' });
await request.auth('/secure', { noRefresh: true });skipAuth,skipRefresh,skipUnauthorizedHandleravailable inconfig.meta.
const [ok, data, err] = await operator(() => request.auth('/api'), {
setOperating: setLoading,
});Two built-in strategies:
import { createCookieStrategy, createTokenStrategy } from '@mints/request';
// Cookie-based (refresh via httpOnly cookie)
createCookieStrategy({ ... });
// Token-exchange (refresh via refresh_token in JS, stored in localStorage by default)
createTokenStrategy({ ... });Both accept an optional storage parameter (memoryStorage, localStorageStorage, or custom).
function useRequest<T, E>(
request: (signal: AbortSignal) => Promise<T>,
deps?: React.DependencyList,
initialValue?: T,
): {
loading: boolean;
data?: T;
error: E | null;
run: () => Promise<T>;
abort: () => void;
};- Use
request.initfor probe token - Use
request.publicfor endpoints that don't require auth. - Use
request.authfor APIs with tokens; retries are automatic. - For cookie-based sessions: set
credentials: 'always'if your backend requireswithCredentials. - Handle
onUnauthorizedglobally (redirect, logout). - Keep refresh handling inside the provided strategies (don't duplicate in your app code).
MIT License © 2025 mints-components