
Security News
Another Round of TEA Protocol Spam Floods npm, But It’s Not a Worm
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.
@alwatr/fetch
Advanced tools
`@alwatr/fetch` is an enhanced, lightweight, and dependency-free wrapper for the native `fetch` API. It provides modern features like caching strategies, request retries, timeouts, and intelligent duplicate request handling, all in a compact package.

@alwatr/fetch is an enhanced, lightweight, and dependency-free wrapper for the native fetch API. It provides modern features like caching strategies, request retries, timeouts, and intelligent duplicate request handling, all in a compact package.
It's designed to be a drop-in replacement for the standard fetch to instantly upgrade your application's network layer.
[Response, null] on success or [null, FetchError] on failure—no exceptions thrown.stale_while_revalidate.bodyJson and queryParams.Install the package using your preferred package manager:
# npm
npm i @alwatr/fetch
# yarn
yarn add @alwatr/fetch
# pnpm
pnpm add @alwatr/fetch
Import the fetch function and use it with tuple destructuring for elegant error handling. The function returns [Response, null] on success or [null, FetchError] on failure—no exceptions are thrown.
import {fetch} from '@alwatr/fetch';
async function fetchProducts() {
console.log('Fetching product list...');
const [response, error] = await fetch('/api/products', {
queryParams: {limit: 10, category: 'electronics'},
cacheStrategy: 'stale_while_revalidate',
timeout: '5s',
});
if (error) {
console.error('Failed to fetch products:', error.message);
console.error('Error reason:', error.reason);
return;
}
// At this point, response is guaranteed to be valid and ok
const data = await response.json();
console.log('Products:', data);
}
fetchProducts();
@alwatr/fetch uses a Go-style tuple return pattern instead of throwing exceptions. This provides explicit, type-safe error handling.
type FetchResponse = Promise<[Response, null] | [null, FetchError]>;
[Response, null] - The response is guaranteed to have response.ok === true[null, FetchError] - Contains detailed information about what went wrongAll errors are returned as FetchError instances, which provide rich context about the failure:
class FetchError extends Error {
reason: FetchErrorReason; // Specific error reason
response?: Response; // The HTTP response (if available)
data?: unknown; // Parsed response body (if available)
}
The reason property indicates why the request failed:
'http_error': HTTP error status (e.g., 404, 500)'timeout': Request exceeded the timeout duration'cache_not_found': Resource not found in cache (when using cache_only)'network_error': Network-level error (e.g., DNS failure, connection refused)'aborted': Request was aborted via AbortSignal'unknown_error': Unspecified errorconst [response, error] = await fetch('/api/user/profile', {
bearerToken: 'jwt-token',
});
if (error) {
switch (error.reason) {
case 'http_error':
console.error(`HTTP ${error.response?.status}:`, error.data);
break;
case 'timeout':
console.error('Request timed out. Please try again.');
break;
case 'network_error':
console.error('Network error. Check your connection.');
break;
case 'cache_not_found':
console.error('Data not available offline.');
break;
default:
console.error('Request failed:', error.message);
}
return;
}
// Safe to use response here
const userData = await response.json();
The fetch function takes a url string and an options object. The options object extends the standard RequestInit and adds several custom options for enhanced control.
| Option | Type | Default | Description |
|---|---|---|---|
method | HttpMethod | 'GET' | The HTTP request method. |
headers | HttpRequestHeaders | {} | An object representing the request's headers. |
timeout | Duration | 8_000 (8s) | Request timeout in milliseconds or as a duration string (e.g., '5s'). Set to 0 to disable. |
retry | number | 3 | Number of retries if the request fails with a server error (5xx) or times out. |
retryDelay | Duration | 1_000 (1s) | Delay between retry attempts in milliseconds or as a duration string. |
removeDuplicate | 'never' | 'always' | 'until_load' | 'auto' | 'never' | Strategy for handling identical parallel requests. body is included for uniqueness. |
cacheStrategy | 'network_only' | 'network_first' | ... | 'network_only' | Caching strategy using the browser's Cache API. |
cacheStorageName | string | 'fetch_cache' | Custom name for the CacheStorage instance. |
revalidateCallback | (response: Response) => void | undefined | Callback executed with the new response when using stale_while_revalidate strategy. |
bodyJson | Json | undefined | A JavaScript object sent as the request body. Sets Content-Type to application/json. |
queryParams | Dictionary | undefined | An object of query parameters appended to the URL. |
bearerToken | string | undefined | A bearer token added to the Authorization header. |
alwatrAuth | {userId: string; userToken: string} | undefined | Alwatr-specific authentication credentials. |
... and all other standard RequestInit properties like signal, credentials, etc.
The queryParams option simplifies adding search parameters to your request URL.
// This will make a GET request to: /api/users?page=2&sort=asc
const [response, error] = await fetch('/api/users', {
queryParams: {page: 2, sort: 'asc'},
});
if (error) {
console.error('Failed to fetch users:', error.message);
return;
}
const users = await response.json();
Use bodyJson to send a JavaScript object as a JSON payload. The Content-Type header is automatically set to application/json.
// This will make a POST request to /api/orders with a JSON body
const [response, error] = await fetch('/api/orders', {
method: 'POST',
bodyJson: {
productId: 'xyz-123',
quantity: 2,
},
});
if (error) {
console.error('Failed to create order:', error.message);
return;
}
const order = await response.json();
console.log('Order created:', order);
Set a timeout for your requests. If the request takes longer than the specified duration, it will be aborted and return a FetchError with reason: 'timeout'.
const [response, error] = await fetch('/api/slow-endpoint', {
timeout: '2.5s', // You can use duration strings
});
if (error) {
if (error.reason === 'timeout') {
console.error('Request timed out after 2.5 seconds');
}
return;
}
The fetch operation will automatically retry on server errors (5xx status codes) or timeouts.
// Retry up to 5 times, with a 2-second delay between each attempt
const [response, error] = await fetch('/api/flaky-service', {
retry: 5,
retryDelay: '2s',
});
if (error) {
console.error('Request failed after 5 retries:', error.message);
return;
}
const data = await response.json();
The removeDuplicate option prevents multiple identical requests from being sent simultaneously. The uniqueness of a request is determined by its method, URL, and body.
'never' (default): Does nothing.'until_load': Caches the Promise of a request until it resolves. Subsequent identical requests will receive a clone of the first response.'always': Caches the response indefinitely (for the lifetime of the application).'auto': Uses 'until_load' if the Cache API is available, otherwise 'always'.// Both calls will result in only ONE network request.
// The second call will receive the response from the first.
const results = await Promise.all([
fetch('/api/data', {removeDuplicate: 'until_load'}),
fetch('/api/data', {removeDuplicate: 'until_load'}),
]);
// Both results will have the same response or error
const [response1, error1] = results[0];
const [response2, error2] = results[1];
Leverage the browser's Cache API with cacheStrategy.
'network_only' (default): Standard fetch behavior; no caching.'cache_first': Serves from cache if available. Otherwise, fetches from the network and caches the result.'network_first': Fetches from the network first. If the network fails, it falls back to the cache.'cache_only': Only serves from cache; returns an error if not found.'update_cache': Fetches from network and updates the cache.'stale_while_revalidate': The fastest strategy. It serves stale content from the cache immediately while sending a network request in the background to update the cache for the next time.// Serve news from cache instantly, but update it in the background for the next visit.
const [response, error] = await fetch('/api/news', {
cacheStrategy: 'stale_while_revalidate',
revalidateCallback: (freshResponse) => {
console.log('Cache updated with fresh data!');
// You can use freshResponse to update the UI if needed
},
});
if (error) {
console.error('Failed to load news:', error.message);
return;
}
const news = await response.json();
Easily add authentication headers with bearerToken or the alwatrAuth scheme.
// Using a Bearer Token
const [response, error] = await fetch('/api/secure/data', {
bearerToken: 'your-jwt-token-here',
});
if (error) {
if (error.response?.status === 401) {
console.error('Authentication failed. Please log in again.');
}
return;
}
const data = await response.json();
// Using Alwatr's authentication scheme
const [response2, error2] = await fetch('/api/secure/data', {
alwatrAuth: {
userId: 'user-id',
userToken: 'user-auth-token',
},
});
The following companies, organizations, and individuals support Nanolib's ongoing maintenance and development. Become a Sponsor to get your logo on our README and website.
Contributions are welcome! Please read our contribution guidelines before submitting a pull request.
FAQs
`@alwatr/fetch` is an enhanced, lightweight, and dependency-free wrapper for the native `fetch` API. It provides modern features like caching strategies, request retries, timeouts, and intelligent duplicate request handling, all in a compact package.
The npm package @alwatr/fetch receives a total of 619 weekly downloads. As such, @alwatr/fetch popularity was classified as not popular.
We found that @alwatr/fetch 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
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.

Security News
PyPI adds Trusted Publishing support for GitLab Self-Managed as adoption reaches 25% of uploads

Research
/Security News
A malicious Chrome extension posing as an Ethereum wallet steals seed phrases by encoding them into Sui transactions, enabling full wallet takeover.