
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
featherquery
Advanced tools
Data fetching, light as a feather. A lightweight React library for fetching, caching, and mutating API data with minimal boilerplate.

Data fetching, light as a feather. A lightweight React library for fetching, caching, and mutating API data with minimal boilerplate.
Modern data-fetching libraries like TanStack Query or RTK Query are powerful, but they often feel heavy and require tons of setup. FeatherQuery is designed to be:
🪶 Lightweight – no clutter, no extra configs
⚡ Fast & efficient – built with caching, refetching, and mutations in mind
🎯 Simple API – just drop in a hook and go
🔧 Extensible – start simple, scale when needed
A React hook that provides access to the appropriate cache instance based on the specified cache mode. Must be used within a QueryProvider context.
import useQueryClient from './useQueryClient';
function MyComponent() {
const { cache } = useQueryClient('session'); // or 'volatile', 'permanent'
// Use cache methods
const data = cache.get(['my-data']);
cache.set(['my-data'], { value: 'test' });
}
volatile (default) - In-memory cache, clears on page refresh
session - Persists for the browser session only
permanent - Persists until manually cleared
cache: Cache - The cache instance for the specified mode
Context Requirement
useFetchIs a lightweight React hook for fetching data from an API in the simplest way possible. It’s ideal when you just want to fetch and display data without setting up complex state management.
The hook returns:
data – the fetched data (or null if not yet loaded)
status – the current status: "IDLE", "LOADING", "SUCCESS", or "ERROR"
error – any error encountered during fetching
isLoading – boolean, true while the request is in progress
isIdle – boolean, true if no request has been made yet
refetch – a function to manually trigger a new request
Optional pollInterval lets you repeatedly fetch data at a given interval. All requests sent from pollInterval refetch data even if it is non-stale. Requests are automatically aborted on refetch or unmount, so you don’t have to worry about race conditions.
const { data, status, error, isLoading, refetch } = useFetch<User[]>('/api/users', {
pollInterval: 5000,
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data?.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
This keeps it beginner-friendly, explains what it returns, and shows a clear minimal usage.
useQueryUsage
const { data, error, status, refetch } = useQuery(
['user', userId],
(signal) => fetchUser(userId, signal),
{
staleTime: 60_000 // data is stale after 1 min,
pollInterval: 10_000 // refetch every 10 secs,
onSuccess: (data) => console.log('Data loaded:', data)
}
);
key: any[] - Unique identifier for cachingfetcher: (signal: AbortSignal) => Promise<T> - Data fetching functionoptions?: QueryOptions<T> - Configuration objectstaleTime: number (default: 30000) - Milliseconds until data is stale
pollInterval: number - Auto-refetch interval in ms
onSuccess: (data: T) => void - Success callback
onError: (error: Error) => void - Error callback
onSettled: (data: T|null, error: Error|null) => void - Completion callback
cacheMode: 'permanent'|'volatile'|'session' - Cache persistence mode
data: T | null - Fetched data or null
error: Error | null - Error object if request failed
status: string - Current status ('STATIC', 'LOADING', 'FETCHING', 'ERROR')
refetch: () => Promise<void> - Manual refetch function
STATIC - Initial state before fetching
LOADING - First fetch in progress
FETCHING - Background refetch with stale data visible
ERROR - Fetch failed with error
Data cached by serialized key array
Stale data served immediately while background refetch occurs
Automatic cache updates on successful fetches
Manual invalidation via key changes or external cache methods
Request deduplication for identical keys
AbortController cancellation for unmounted components
Memoized key comparison to prevent unnecessary re-renders
Background polling without UI state changes
Stale-while-revalidate pattern for smooth UX
useMutationA React hook for handling data mutations (create, update, delete operations) with built-in optimistic updates, retry logic, and cache management. Perfect for forms, actions, and any data-modifying operations.
function CreatePost() {
const { mutate, isLoading, error } = useMutation(
{
url: 'your-link/api/posts',
method: 'POST',
},
{
onSuccess: (data) => console.log('Post created:', data),
onError: (error) => console.error('Creation failed:', error),
optimisticUpdate: (cache, variables) => {
// Optimistically update cache
},
}
);
const handleSubmit = (formData) => {
mutate(formData);
};
return (
<form onSubmit={handleSubmit}>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Creating...' : 'Create Post'}
</button>
{error && <div>Error: {error.message}</div>}
</form>
);
}
useMutation<TResponse, TError, TVariables>(
config: Config<TResponse, TVariables>,
options?: Options<TResponse, TError, TVariables>
)
(NOTE: Provide either mutateFn or url + method.)
mutateFn?: (variables: TVariables) => Promise<TResponse> - Custom mutation function
url?: string - Endpoint URL (alternative to mutateFn)
method?: string - HTTP method (GET, POST, PUT, DELETE, etc.)
headers?: HeadersInit - Custom request headers
onSuccess?: (response: TResponse, variables: TVariables) => void - Success callback
`onError?: (error: TError, variables: TVariables) => void - Error callback
onSettled?: (response: TResponse|null, error: TError|null, variables: TVariables) => void - Completion callback
invalidateKeys?: any[] - Query keys to invalidate after mutation
optimisticUpdate?: (cache: Cache, variables: TVariables) => void - Pre-emptive cache update
rollback?: (cache: Cache, variables: TVariables) => void - Rollback function for failed optimistic updates
retries?: number - Number of retry attempts (default: 0)
`retryDelay?: (attempt: number) => number - Custom retry delay function
cacheMode?: 'permanent'|'volatile'|'session' - Cache persistence mode
const { mutate } = useMutation(
{ url: '/api/like', method: 'POST' },
{
optimisticUpdate: (cache, variables) => {
cache.set(['post', variables.postId], (old) => ({
...old,
likes: old.likes + 1,
}));
},
rollback: (cache, variables) => {
cache.set(['post', variables.postId], (old) => ({
...old,
likes: old.likes - 1,
}));
},
}
);
Configure automatic retries for failed requests
Supports custom delay strategies
useMutation(
{ url: '/api/upload', method: 'POST' },
{
retries: 3,
retryDelay: (attempt) => 1000 * attempt, // 1s, 2s, 3s delays
}
);
useMutation(
{ url: '/api/comments', method: 'POST' },
{
invalidateKeys: ['posts', 'comments'],
// Invalidates both posts and comments queries after mutation
}
);
const { mutate } = useMutation(
{ url: '/api/user', method: 'POST' },
{
onSuccess: (data, variables) => {
showNotification('User created successfully');
},
onError: (error, variables) => {
showError(`Failed: ${error.message}`);
logError(error, variables);
},
onSettled: (data, error, variables) => {
trackAnalytics('user_creation', { success: !error, variables });
}
}
);
Automatic cache updates through optimistic updates
Selective cache invalidation via invalidateKeys
Rollback capability for failed optimistic updates
Cache mode support for different persistence needs
useInfiniteQueryuseInfiniteQuery is a React hook for handling infinite scrolling or paginated data fetching. It abstracts away the complexity of managing page state, caching, and stale data so you can focus on rendering your list.
Fetch the next page or previous page on demand.
Cache pages to avoid unnecessary re-requests.
Automatically detect and refetch stale data.
Cancel ongoing requests to prevent race conditions.
Optionally start with an initial fetch (initialFetch).
getPreviousPageParam?: (firstPageParam: string | number) => any - Function to get previous page parameter
initialFetch?: boolean - Whether to fetch initial page automatically (default: false)
staleTime?: number - Time in ms before data is considered stale (default: 30000)
onSuccess?: (data: PagedDataEntry<T>) => void - Success callback
onError?: (error: Error) => void - Error callback
onSettled?: (data: PagedDataEntry<T>|null, error: Error|null) => void - Completion callback
cacheMode?: 'permanent'|'volatile'|'session' - Cache persistence mode (default: 'volatile')
Returns an object with:
data: { pages: T[][], pageParams: any[] } - All loaded pages and their parameters
error: Error | null - Error object if any request failed
status: string - Current status ('IDLE', 'LOADING', 'SUCCESS', 'ERROR')
fetchNextPage: () => void - Function to load the next page
fetchPreviousPage: () => void - Function to load the previous page
// start from page 1 (number)
const {
data,
status,
error,
fetchNextPage,
fetchPreviousPage,
} = useInfiniteQuery(
1, // <-- initial page param (number)
(page, signal) =>
fetch(`/api/messages?page=${page}`, { signal }).then((res) => res.json()),
(lastPageParam) => lastPageParam + 1, // next page number
{
getPreviousPageParam: (firstPageParam) =>
firstPageParam > 1 ? firstPageParam - 1 : undefined,
initialFetch: true,
}
);
Short rationale / tips:
The first argument here is the initial page param (a number in page-number APIs).
getNextPageParam receives the last used page param (a number) and returns the next one.
Make getPreviousPageParam return undefined when there is no previous page (so you can stop fetching backwards)
FAQs
Data fetching, light as a feather. A lightweight React library for fetching, caching, and mutating API data with minimal boilerplate.
We found that featherquery demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.