fdt-request-shield
v2.0.1
Published
API call shield for in-flight dedupe, TTL cache, and request control.
Maintainers
Readme
fdt-request-shield
Shield products are created by Future Dev Tech for modern frontend apps.
fdt-request-shield is a TypeScript-first API call shield package for modern frontend apps. It helps with:
- API request deduplication
- smart TTL cache for API responses
- page-level and global request scope
- cache invalidation and cleanup
- retry and timeout control
- safe wrapper usage around your existing API functions
This package is framework agnostic. You can use the same methods in:
- Next.js
- React
- Vite
- plain TypeScript frontend apps
Why Use Request Shield
Sometimes the same API call runs many times because the component renders again or the same request is triggered from more than one place. That creates extra load on the server instead of making only one API call.
This usually happens when:
- the page renders again
- React runs the component more than once in development
- two or more components call the same API at the same time
- the user clicks the same button many times
- search, filter, or input values change
When we say the API is "triggered multiple times", we mean:
- the same request is started again
- the same data is fetched again
- the browser sends extra network calls
- the server gets unnecessary repeated requests
fdt-request-shield solves this by:
- sending only one request for the same key at the same time
- using cached data again if it is still fresh
- running a new API call only when the key changes
- sharing the same request across components when needed
- letting you clear cache when you want fresh data
This gives you:
- fewer duplicate API calls
- less server load
- faster response in the UI
- cleaner API handling in frontend apps
Install
npm install fdt-request-shieldPublic Methods
| Method | Type | Params | Use |
| --- | --- | --- | --- |
| requestOnce | function | (key: string, fn: () => Promise<T>) | Dedupes only in-flight requests |
| cachedRequest | function | (key: string, fn: () => Promise<T>, options: { ttl: number }) | Adds TTL cache plus in-flight dedupe |
| shieldRequest | function | (options: { key: string; fn: () => Promise<T>; ttl?: number; scope?: 'page' \| 'global'; retry?: number; timeout?: number; signal?: AbortSignal }) | Full wrapper with cache, scope, retry, timeout |
| clearCache | function | (key?: string) | Clears one key or the full cache/in-flight store |
| invalidate | function | (prefix: string) | Clears all entries that start with a prefix |
| getCacheStats | function | () | Returns cache and in-flight stats |
requestOnce(key, fn)
Use this when you want only in-flight deduplication.
const data = await requestOnce('months-list', () => getMonthList());Best for:
- duplicate button clicks
- repeated renders
- multiple components requesting the same data at the same time
cachedRequest(key, fn, { ttl })
Use this when you want dedupe plus TTL-based caching.
const data = await cachedRequest(
`location-${country}-${state}-${trip}`,
() => locationOtherDetails(queryString),
{ ttl: 30000 },
);Best for:
- search results
- filter-based API calls
- data that should stay fresh for a short time
shieldRequest({ key, fn, ttl, scope, retry, timeout, signal })
Use this when you want the full control layer.
const data = await shieldRequest({
key: ['travel-leads', search, budgetMin, budgetMax, interestType, sortBy].join('|'),
fn: () => leadsList('', search),
ttl: 30000,
scope: 'page',
retry: 1,
timeout: 10000,
});Best for:
- production screens
- shared component data
- large forms
- API calls with retry and timeout needs
Utility Methods
clearCache(key?)
Clear everything or a single key.
clearCache();
clearCache('months-list');invalidate(prefix)
Clear all cache entries and in-flight keys that start with a prefix.
invalidate('travel-leads');getCacheStats()
Inspect current cache and in-flight counts.
const stats = getCacheStats();
console.log(stats);Scope
scope: 'page'
Use page scope when you want isolation inside one page or one component flow.
scope: 'global'
Use global scope when you want cache sharing across the app runtime.
Retry and Timeout
retry
Retry means try again when the request fails.
retry: 1This means:
- first attempt can fail
- one more attempt is allowed
timeout
Timeout means stop waiting after a specific time.
timeout: 30000This means:
- wait up to 30 seconds
- if the request is still running, treat it as timed out
If you do not pass timeout, the request can continue until it finishes.
Framework Examples
Next.js
'use client';
import { useEffect, useState } from 'react';
import { shieldRequest } from 'fdt-request-shield';
export function MonthsPanel() {
const [items, setItems] = useState<any[]>([]);
useEffect(() => {
shieldRequest({
key: 'months-list',
fn: () => getMonthList(),
ttl: 30000,
scope: 'page',
}).then(setItems);
}, []);
return (
<ul>
{items.map((month) => (
<li key={month.id}>{month.name}</li>
))}
</ul>
);
}React
import { useEffect, useState } from 'react';
import { cachedRequest } from 'fdt-request-shield';
export function UserList() {
const [users, setUsers] = useState<any[]>([]);
useEffect(() => {
cachedRequest('users-list', () => fetchUsers(), { ttl: 60000 }).then(setUsers);
}, []);
return <pre>{JSON.stringify(users, null, 2)}</pre>;
}Vite
import { requestOnce } from 'fdt-request-shield';
const data = await requestOnce('dashboard-metrics', () => fetchDashboardMetrics());Behavior Summary
- same key = dedupe/cache logic applies
- different key = new API call
- same key during an active request = same promise returned
- expired TTL = API runs again and cache refreshes
pagescope = isolated request memoryglobalscope = shared request memory
Keywords Cover
request dedupe package, api cache package, ttl cache frontend, in-flight request deduplication, react api caching, nextjs request cache, vite api wrapper, typescript request cache, api call shield, smart cache for frontend
License
MIT
Created by Future Dev Tech.
- GitHub: https://github.com/futuredevtech
- Instagram: https://www.instagram.com/futuredevtech48/
- LinkedIn: https://www.linkedin.com/in/abhimanyu-sinhmar-672b6b2b2
