ManualRetryPlugin
Stores terminal failures in MANUAL mode and lets you replay them on demand — on reconnect, after user action, or on app resume. Built-in idempotency safeguards and age limits prevent unsafe replay.
Install
import { createManualRetryPlugin } from 'axios-retryer/plugins/ManualRetryPlugin'; Basic usage
import { createRetryer, RETRY_MODES } from 'axios-retryer';
import { createManualRetryPlugin } from 'axios-retryer/plugins/ManualRetryPlugin';
const manualRetry = createManualRetryPlugin({
maxRequestsToStore: 50,
manualRetryMaxAge: 5 * 60_000, // Drop requests older than 5 min
});
const retryer = createRetryer({ mode: RETRY_MODES.MANUAL }).use(manualRetry);
// Request fails in manual mode → stored
try {
await retryer.axiosInstance.post('/api/order', orderData);
} catch {
showToast('Order saved — will retry when online');
}
// Later — replay on reconnect
window.addEventListener('online', async () => {
const results = await manualRetry.retryFailedRequests();
console.log(`Replayed ${results.length} request(s)`);
}); All options
createManualRetryPlugin({
maxRequestsToStore: 100, // Max stored failures (oldest evicted first; default: 100)
manualRetryMaxAge: 300_000, // ms before stored requests are discarded (default: none)
storeNonIdempotent: false, // Store POST/PUT/PATCH/DELETE without idempotency key (default: false)
storeAuthRequests: false, // Store requests with auth headers (default: false — safer)
// Mutate or redact a request before storing
prepareRequestForStore: (config) => ({
...config,
data: { redacted: true }, // Strip sensitive payload
}),
// Re-attach fresh auth before replay
rehydrateAuth: (config) => {
config.headers ??= {};
config.headers['Authorization'] = `Bearer ${store.getAccessToken()}`;
return config;
},
// Filter which requests to replay
beforeRetry: (config) => {
if (config.url?.includes('/non-critical')) return null; // Skip
return config;
},
}) Plugin methods
// Replay all stored failures
const results = await manualRetry.retryFailedRequests();
// Inspect what's stored without replaying
const pending = manualRetry.getStoredRequests();
console.log(`${pending.length} pending requests`);
// Drop stored requests without replaying
manualRetry.clearStoredRequests(); Offline-first SPA pattern
import { createRetryer, RETRY_MODES } from 'axios-retryer';
import { createManualRetryPlugin } from 'axios-retryer/plugins/ManualRetryPlugin';
const manualRetry = createManualRetryPlugin({
manualRetryMaxAge: 60_000,
storeNonIdempotent: false, // Only safe idempotent requests
rehydrateAuth: (config) => {
// Always use the latest token when replaying
config.headers ??= {};
config.headers['Authorization'] = `Bearer ${auth.getToken()}`;
return config;
},
});
const retryer = createRetryer({ mode: RETRY_MODES.MANUAL }).use(manualRetry);
async function submitForm(data: FormData) {
try {
await retryer.axiosInstance.put('/api/profile', data, {
headers: { 'Idempotency-Key': crypto.randomUUID() },
});
showSuccess('Saved');
} catch {
showWarning('Offline — will sync when reconnected');
}
}
// Sync on reconnect
navigator.connection?.addEventListener('change', async () => {
if (navigator.onLine) {
const synced = await manualRetry.retryFailedRequests();
showToast(`Synced ${synced.length} pending changes`);
}
}); Security defaults
| Default | What it prevents |
|---|---|
storeAuthRequests: false | Storing requests with auth headers; prevents replaying expired credentials |
storeNonIdempotent: false | Storing POST/PUT/PATCH/DELETE without idempotency key; prevents duplicate side effects |
No rehydrateAuth by default | Replayed requests carry no auth unless you explicitly provide fresh tokens |