
Security News
New Study Identifies 53 Slopsquatting Targets Across 5 Frontier LLMs
Five frontier LLMs generated the same nonexistent package names, leaving 53 available for potential slopsquatting across PyPI and npm.
fetch-extras
Advanced tools
Useful utilities for working with Fetch
For more features and conveniences on top of Fetch, check out my ky package.
npm install fetch-extras
import {withHttpError, withTimeout} from 'fetch-extras';
// Create an enhanced reusable fetch function that:
// - Throws errors for non-2xx responses
// - Times out after 5 seconds
const enhancedFetch = withHttpError(withTimeout(fetch, 5000));
const response = await enhancedFetch('/api');
const data = await response.json();
Error class thrown when a response has a non-2xx status code.
import {HttpError, throwIfHttpError} from 'fetch-extras';
try {
await throwIfHttpError(fetch('/api'));
} catch (error) {
if (error instanceof HttpError) {
console.log(error.response.status); // 404
}
}
Throws an HttpError if the response is not ok. Can also accept a promise that resolves to a response.
import {throwIfHttpError} from 'fetch-extras';
const response = await throwIfHttpError(fetch('/api'));
const data = await response.json();
Returns a wrapped fetch function that automatically throws HttpError for non-2xx responses.
import {withHttpError} from 'fetch-extras';
const fetchWithError = withHttpError(fetch);
const response = await fetchWithError('/api');
Returns a wrapped fetch function with timeout functionality.
import {withTimeout} from 'fetch-extras';
const fetchWithTimeout = withTimeout(fetch, 5000);
const response = await fetchWithTimeout('/api');
Paginate through API responses using async iteration. By default, it automatically follows RFC 5988 Link headers with rel="next".
Returns an async iterator that yields items from each page.
import {paginate} from 'fetch-extras';
// Basic usage with Link headers (GitHub API)
for await (const commit of paginate('https://api.github.com/repos/sindresorhus/ky/commits')) {
console.log(commit.sha);
}
Type: object
Type: object
Type: (response: Response) => Promise<unknown[]>
Default: response => response.json()
Transform the response into an array of items.
for await (const user of paginate('https://api.example.com/users', {
pagination: {
transform: async response => {
const data = await response.json();
return data.users; // Extract from nested property
}
}
})) {
console.log(user);
}
Type: (data: {response, currentUrl, currentItems, allItems}) => Promise<PaginationNextPage | false>
Default: Parses RFC 5988 Link header
Determine the next page to fetch. Return an object with fetch options for the next request, or false to stop pagination.
[!IMPORTANT] The response body has already been consumed by the
transformfunction. Do NOT callresponse.json()or other body methods here. Extract pagination info from headers, the URL, or share data from the transform function through closure.
[!NOTE] Returning
headersreplaces all inherited headers, consistent with standard Fetch API behavior. If you need to add headers while keeping existing ones, read them from the response and include them in the returned object. Settingbodytoundefinedwill strip body-related headers (Content-Type,Content-Length, etc.) from the request, consistent with HTTP semantics for bodyless requests.
// Cursor-based pagination using headers (recommended)
for await (const item of paginate('https://api.example.com/items', {
pagination: {
paginate: ({response}) => {
const cursor = response.headers.get('X-Next-Cursor');
return cursor
? {url: new URL(`https://api.example.com/items?cursor=${cursor}`)}
: false;
}
}
})) {
console.log(item);
}
// Sharing data between transform and paginate via closure
let nextCursor;
for await (const item of paginate('https://api.example.com/items', {
pagination: {
transform: async (response) => {
const data = await response.json();
nextCursor = data.nextCursor;
return data.items;
},
paginate: () => {
return nextCursor
? {url: new URL(`https://api.example.com/items?cursor=${nextCursor}`)}
: false;
}
}
})) {
console.log(item);
}
Type: (data: {item, currentItems, allItems}) => boolean
Default: () => true
Filter items before yielding them.
// Only get active users
for await (const user of paginate('https://api.example.com/users', {
pagination: {
filter: ({item}) => item.status === 'active'
}
})) {
console.log(user);
}
Type: (data: {item, currentItems, allItems}) => boolean
Default: () => true
Check if pagination should continue after yielding an item. This is called after filter returns true. Useful for stopping pagination based on item values.
// Stop when we reach items older than one week
const oneWeekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
for await (const commit of paginate('https://api.github.com/repos/user/repo/commits', {
pagination: {
shouldContinue: ({item}) => new Date(item.date).getTime() >= oneWeekAgo
}
})) {
console.log(commit);
}
Type: number
Default: Infinity
Maximum number of items to yield.
const items = await paginate.all('https://api.example.com/items', {
pagination: {
countLimit: 100 // Stop after 100 items
}
});
Type: number
Default: 10000
Maximum number of requests to make. This prevents infinite loops if your paginate function has bugs. Ensure your paginate function eventually returns false or the iteration will continue until this limit is reached.
Type: number
Default: 0
Delay in milliseconds between requests. Useful for rate limiting.
for await (const item of paginate('https://api.example.com/items', {
pagination: {
backoff: 1000 // Wait 1 second between requests
}
})) {
console.log(item);
}
Type: boolean
Default: false
Whether to keep all yielded items in memory. When true, the allItems array passed to callbacks will contain all previously yielded items. When false, allItems will always be empty to save memory.
Type: (input: RequestInfo | URL, init?: any) => Promise<Response>
Default: globalThis.fetch
Custom fetch function to use for requests. This allows you to use a custom fetch implementation, such as ky, or a fetch function wrapped with withHttpError or withTimeout.
import {paginate} from 'fetch-extras';
import ky from 'ky';
const url = 'https://api.github.com/repos/sindresorhus/ky/commits';
for await (const commit of paginate(url, {fetchFunction: ky})) {
console.log(commit.sha);
}
Get all paginated items as an array. This is a convenience method that collects all items into memory. For large datasets, prefer using the async iterator directly.
import {paginate} from 'fetch-extras';
const commits = await paginate.all('https://api.github.com/repos/sindresorhus/ky/commits', {
pagination: {
countLimit: 50
}
});
console.log(`Fetched ${commits.length} commits`);
FAQs
Useful utilities for working with Fetch
The npm package fetch-extras receives a total of 20,117 weekly downloads. As such, fetch-extras popularity was classified as popular.
We found that fetch-extras 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
Five frontier LLMs generated the same nonexistent package names, leaving 53 available for potential slopsquatting across PyPI and npm.

Security News
The White House’s Gold Eagle Initiative aims to coordinate AI-discovered vulnerabilities, validate findings, and accelerate patching across critical software.

Security News
A Shai-Hulud infection exposed Suno's source code, which shows the AI music startup stream-ripped tracks to train its models.