npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

stormfetch

v1.13.0

Published

A typed HTTP client for React, React Native, Expo, browsers, Node.js, and SSR.

Downloads

370

Readme

StormFetch

Typed HTTP client for React, React Native, Expo, browsers, Node.js, and SSR.

StormFetch provides one TypeScript API for web and mobile applications without adding a runtime dependency. It includes typed requests, interceptors, retries, caching, request deduplication, cancellation, uploads, downloads, React hooks, runtime-aware adapters, plugins, and normalized errors.

  • Version: 1.13.0
  • Developer: Pradeep Kumar Sheoran
  • Company: BSG Technologies
  • Contact: +91-8595147850
  • Website: https://bsgtechnologies.com

Why StormFetch?

  • One client for ReactJS, React Native, Expo, Node.js, Deno, Bun, and SSR.
  • Strong request body and response typing.
  • No React, React Native, or UI library dependency.
  • Fetch, browser XHR, Node HTTP/1.1, and Node HTTP/2 adapters.
  • Dedicated stormfetch/react-native entry point.
  • Auth, refresh-token, language, retry, cache, offline queue, and plugin support.
  • Security policy layer for host allowlists, blocked hosts, HTTPS enforcement, private-network blocking, method allowlists, XSRF origin checks, request signing, and redacted debug exports.
  • Tree-shakeable ESM plus CommonJS and TypeScript declarations.

Installation

npm install stormfetch

Compatibility

| Runtime | Import | Automatic adapter | Notes | | --- | --- | --- | --- | | ReactJS / browser | stormfetch | Fetch; XHR when progress is requested | Browser download and persistent web storage supported | | React Native / Expo | stormfetch/react-native | Fetch | Native files, online state, and persistence use injected app adapters | | Node.js 18+ | stormfetch | Node HTTP/HTTPS | Native FormData, proxy, streams, redirects, progress, and size controls supported | | Node.js HTTP/2 | stormfetch | adapter: 'http2' | Uses Node's built-in node:http2 client | | Deno | stormfetch | Fetch | Official fetch-adapter runtime path | | Bun | stormfetch | Fetch | Official fetch-adapter runtime path | | SSR | stormfetch | Runtime-selected | Browser globals are guarded |

React is not bundled. createStormFetchHooks receives your installed React object, so the same hook factory works with React DOM and React Native.

Quick Start: ReactJS

// api.ts
import { createStormFetchClient } from 'stormfetch';

export const api = createStormFetchClient({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 30_000,
  token: () => localStorage.getItem('accessToken'),
  language: () => localStorage.getItem('language') ?? 'en',
  onUnauthorized: () => {
    localStorage.removeItem('accessToken');
    window.location.assign('/login');
  },
});
interface User {
  id: number;
  name: string;
}

const response = await api.get<User[]>('/users', {
  params: { page: 1, active: true },
  cache: true,
  cacheTTL: 60_000,
});

console.log(response.data);

Quick Start: React Native / Expo

// api.ts
import { createReactNativeStormFetchClient } from 'stormfetch/react-native';

export const api = createReactNativeStormFetchClient({
  baseURL: 'https://api.example.com',
  timeout: 30_000,
  token: async () => getAccessTokenFromSecureStorage(),
  isOnline: async () => getCurrentNetworkState(),
  onUnauthorized: () => navigateToLogin(),
  fileSaver: async (blob, fileName) => {
    await saveWithYourNativeFileSystem(blob, fileName);
  },
});

The native factory pins automatic requests to fetch. This prevents a React Native process shim from being mistaken for Node.js.

React and React Native Hooks

import * as React from 'react';
import { createStormFetchHooks } from 'stormfetch';
import { api } from './api';

const { useStormQuery, useLazyStormQuery, useStormMutation, useStormInfiniteQuery } =
  createStormFetchHooks(React, api);

export function UsersScreen() {
  const users = useStormQuery<User[]>('/users');
  const createUser = useStormMutation<User, { name: string }>('/users', 'post', {
    invalidateTags: ['users'],
  });

  if (users.loading) return null;

  return (
    <button onClick={() => createUser.mutate({ name: 'Pradeep' })}>
      Create user
    </button>
  );
}

Replace the JSX with View, Text, and Pressable in React Native; the hook setup remains the same.

Client Methods at a Glance

| Method | What it does | Typical use | | --- | --- | --- | | request(config) | Sends any supported HTTP method | Dynamic or shared request builders | | fetch(url, config?) | Fetch-like universal request | One familiar method for any API | | get(url, config?) | Reads data | Lists, details, search | | post(url, data?, config?) | Creates or triggers data | Forms, login, commands | | put(url, data?, config?) | Fully replaces data | Full record update | | patch(url, data?, config?) | Partially updates data | Edit one or more fields | | delete(url, config?) | Deletes data | Record removal | | head(url, config?) | Reads headers only | File/auth/CDN checks | | options(url, config?) | Reads server capabilities | CORS/method discovery | | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS | Uppercase aliases | HTTP-style SDKs | | postJson(url, data) | Posts JSON explicitly | APIs that require JSON headers | | postForm(url, data) | Posts URL encoded form data | OAuth, login, legacy APIs | | postMultipart(url, data) | Posts multipart form data | Uploads | | graphql(query, variables?, config?) | Sends a GraphQL operation | GraphQL APIs | | batch(requests) | Runs many requests together | Dashboards, bootstrapping | | poll(url, options) | Repeats a GET until stopped | Job/payment/order status | | getHtml, getXml, getDom, scrape | Fetch and parse content | HTML/XML pages, feeds, scraping | | resource(basePath) | Creates CRUD helpers | REST resources | | cacheManager | Prefetch, hydrate, dehydrate, inspect keys | Smart app cache | | history, replay, toCurl, toHAR | Debug and reproduce requests | DevTools-style workflows | | policy(match, config) | Applies request defaults by route | Payments/admin/API rules | | streamText, streamJson, streamNdjson, sse | Reads streaming APIs | AI, logs, live events | | contract(definition) | Builds typed service functions | SDK-like API layer | | download(url, name?, config?) | Fetches a Blob and optionally saves it | Reports, invoices, exports | | createFormData(data) | Builds multipart data | Images, documents, mixed fields | | clearCache(key?) | Clears all or matching cache entries | Logout, refresh, invalidation | | clearCacheAsync(key?) | Clears cache and awaits async storage | AsyncStorage/MMKV wrappers | | invalidateTags(tags) | Clears tagged cache entries | Mutation-driven refresh | | flushOfflineQueue() | Runs queued requests | Reconnect handling |

Complete signatures and every exported helper are documented in API_REFERENCE.md. Native setup and limitations are documented in REACT_NATIVE.md.

Function Selection Guide

Use this table when you know the job but not the StormFetch function name.

| User goal | Use these functions/options | What to configure | | --- | --- | --- | | Build one shared API client | createStormFetchClient | baseURL, timeout, token, retry, cache, plugins, security | | Build a mobile API client | createReactNativeStormFetchClient | async token, isOnline, fileSaver, async cache storage | | Build an SSR/server API client | createStormFetchServerClient | incoming headers/cookies, server baseURL, Node transport options | | Add typed routes | createTypedStormFetchClient, contract | endpoint map or contract definition with response/body types | | Read API data | get, GET, fetch | params, cache, cacheTTL, cacheTags, dedupe | | Write API data | post, put, patch, delete | body data, invalidateTags, schema validators, retry policy | | Upload files | createFormData, postMultipart, createReactNativeFile | browser File/Blob or React Native { uri, name, type } | | Download files | download, saveBlob, native fileSaver | responseType: 'blob', file name, native saver for mobile | | Call GraphQL | graphql | query string, variables, endpoint override if needed | | Load many endpoints together | batch | array of request configs; responses keep the same order | | Track long-running jobs | poll | interval, stop condition, timeout/cancel signal | | Generate CRUD helpers | resource | base path such as /users | | Cache and preload screens | cacheManager.prefetch, dehydrateCache, hydrateCache | cache TTL, stale time, tags, SSR snapshot | | Clear stale data after mutation | invalidateTags, clearCache, clearCacheAsync | tag names or cache key prefix | | Add React state hooks | createStormFetchHooks | pass your React object and configured client | | Debug a production issue | history, replay, toCurl, toHAR, subscribe | keep redaction enabled; attach request ID to support logs | | Add route-specific rules | policy | matcher plus timeout/retry/cache/security defaults | | Protect failing backends | circuitBreaker | threshold, cooldown, fallback response | | Read streaming responses | streamText, streamJson, streamNdjson, sse | stream parser, event handlers, cancel signal | | Parse websites or feeds | getHtml, getXml, getDom, scrape | CSS selectors or custom DOM parser | | Mock APIs in tests | createMockAdapter, createMockServer | routes, scenarios, delays, errors, request assertions | | Harden request safety | security, hmacSigningPlugin, secureLoggerPlugin | host/CIDR policy, HTTPS, credential forwarding, signing | | Use Node HTTP/2 | adapter: 'http2' | Node runtime and an HTTP/2-capable server | | Use Deno or Bun | automatic fetch adapter | import from stormfetch; runtime is detected automatically |

Complete Feature Inventory

This is the full old and new capability list that npm users can scan quickly.

| Group | Features included | | --- | --- | | Core HTTP | request, fetch, lowercase methods, uppercase method aliases, JSON/form/multipart helpers, GraphQL, batch, polling | | Typed SDK building | typed endpoint maps, resource, contract, OpenAPI service generator, schema validation hooks | | Runtime support | ReactJS, React Native, Expo, browser, SSR, Node.js, Deno, Bun | | Adapters | Fetch, XHR, Node HTTP/1.1, Node HTTP/2, mock adapter, custom adapters | | React usage | query hook, lazy query hook, mutation hook, infinite query hook, optimistic mutation helpers | | Mobile usage | React Native entry point, async token provider, online-state hook, native file descriptor, injected file saver | | Cache | memory/local/custom/async cache, TTL, stale refresh, tags, prefetch, hydrate, dehydrate, cache key inspection | | Reliability | timeout, cancellation, retries, safe retry policy, dedupe, priority queue, concurrency limits, offline queue, circuit breaker | | Debugging | request IDs, duration, request history, replay, curl export, HAR export, structured events | | Content tools | HTML/XML fetch, DOM parsing, scraping, metadata extraction, link/image/json-ld extraction | | Streaming | text stream, JSON stream, NDJSON stream, Server-Sent Events | | Upload/download | browser and Node FormData, React Native file object, multipart upload, Blob download, native save callback | | Security | XSRF, auth helpers, host allow/block, CIDR/DNS checks, private-network blocking, metadata blocking, HTTPS guard, path traversal guard | | Advanced security | credential forwarding policy, redirect credential stripping, no-proxy normalization, JSON/query caps, decompression guard, TLS/mTLS, certificate pinning | | Security plugins | HMAC signing, secure logger, suspicious auth-status burst detection | | Supply-chain process | security smoke test, threat model, release checklist, denylist, zizmor, CodeQL, Semgrep, dependency review, SBOM, OIDC provenance publish workflow |

Features Added in Today's Advanced Upgrade

This release makes StormFetch more than a normal HTTP client. These are the major advanced features now visible to npm users:

  • Universal HTTP methods: fetch, get, post, put, patch, delete, head, options
  • Uppercase HTTP aliases: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
  • Data submit helpers: postJson, postForm, postMultipart
  • GraphQL helper: graphql(query, variables)
  • Multi-request orchestration: batch(requests) and poll(url, options)
  • REST SDK generator: resource(basePath) and contract(definition)
  • React and React Native hooks: query, lazy query, mutation, infinite query
  • Smart cache manager: prefetch, cache tags, stale refresh, hydrate, dehydrate, key inspection
  • Debug tools: request history, replay, toCurl, and toHAR
  • Route policy engine: per-route retry, timeout, headers, cache, and security rules
  • Reliability layer: retry policies, request dedupe, queue priority, circuit breaker fallback
  • HTML/XML tools: getHtml, getXml, getDom, scrape, metadata/link/image/json-ld extraction
  • Streaming tools: streamText, streamJson, streamNdjson, and sse
  • Upload/download helpers for browser and React Native
  • Mock testing tools: createMockAdapter and scenario-based createMockServer
  • SSR helper: createStormFetchServerClient with cookie/header forwarding
  • Security and observability: HTTPS guard, host/CIDR/DNS policy, private-network and metadata blocking, path traversal guards, no-proxy handling, redirect credential controls, request signing, redacted history/curl/HAR, structured security events

Reliability Release: 1.13.0

StormFetch 1.13.0 adds the public hardening layer around the transport work:

  • Structured StormFetchError objects now include code, isStormFetchError, request, and safe toJSON() output.
  • Progress events include bytes, rate, estimated, upload, and download while keeping existing fields.
  • Node maxRate supports practical upload/download throttling.
  • responseEncoding, timeoutErrorMessage, transport, env, fetchOptions, and advanced Node networking options are typed.
  • Invalid critical options reject with ERR_BAD_OPTION or ERR_BAD_OPTION_VALUE.
  • Responses/errors expose the low-level request object where practical.

Reliability Release: 1.12.0

StormFetch 1.12.0 closes the next Axios-replacement transport gaps found during production-readiness testing:

  • Node HTTP adapter serializes native WHATWG FormData automatically for multipart uploads.
  • Node stream uploads and responseType: 'stream' response bodies are supported by the HTTP/HTTPS adapter.
  • gzip, deflate, brotli, and Node-supported zstd response decompression run before parsing.
  • Async JSON/response parse failures reject with a StormFetch parse error instead of escaping the promise chain.
  • External AbortController cancellation is classified as isAbortError, not isNetworkError.
  • 301/302 POST and 303 redirects rewrite to GET and drop request bodies.
  • HTTPS requests through HTTP/HTTPS proxies use CONNECT tunneling.
  • httpAgent and httpsAgent can be supplied per client or request.
  • formSerializer: 'urlencoded' | 'multipart' | 'json' | 'auto' supports automatic form body serialization.
  • Public npm run reliability:test smoke coverage and a Node 18/20/22 CI matrix are included.

Reliability Release: 1.11.0

StormFetch 1.11.0 closed the first Axios comparison gaps:

  • Node HTTP adapter serializes native WHATWG FormData automatically for multipart uploads.
  • Buffered Node uploads emit chunked progress events instead of only a final 100% callback.
  • External AbortController cancellation is classified as isAbortError, not isNetworkError.
  • Browser upload progress uses the XHR adapter even when adapter: 'fetch' is requested with onUploadProgress.

Security Release: 1.10.0

StormFetch 1.10.0 adds deeper runtime, transport, payload, and supply-chain hardening.

import { createStormFetchClient } from 'stormfetch';

export const api = createStormFetchClient({
  baseURL: 'https://api.example.com',
  token: () => localStorage.getItem('accessToken'),
  withXSRFToken: true,
  security: {
    allowedHosts: ['api.example.com', '*.trusted.example.com'],
    blockedHosts: ['metadata.google.internal', '169.254.169.254'],
    blockedCidrs: ['169.254.169.254/32', '127.0.0.0/8', '10.0.0.0/8'],
    allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
    allowAbsoluteUrls: false,
    allowPathTraversal: false,
    allowHttp: false,
    allowPrivateNetwork: false,
    blockCloudMetadata: true,
    validateDns: true,
    maxUrlLength: 4096,
    maxQueryDepth: 5,
    maxJsonDepth: 24,
    maxJsonKeys: 10_000,
    maxResponseJsonDepth: 24,
    maxResponseJsonKeys: 10_000,
    maxDecompressionRatio: 100,
    sensitiveHeaders: ['x-tenant-secret'],
    credentialPolicy: {
      authorization: ['api.example.com'],
      cookie: 'same-origin',
      'x-api-key': 'never',
    },
    signRequest: (config) => {
      config.headers['X-Client'] = 'web';
    },
  },
});
// Node HTTP/2
const response = await api.get('/metrics', { adapter: 'http2' });

// Enterprise proxy bypass
const enterprise = createStormFetchClient({
  proxy: {
    protocol: 'http',
    host: 'proxy.corp.local',
    port: 8080,
    noProxy: ['localhost', '.internal.example.com', '169.254.169.254'],
  },
});

// Redirect credential safety
await api.get('/download', {
  beforeRedirect: ({ from, to, requestOptions }) => {
    if (from.origin !== to.origin) {
      delete (requestOptions.headers as Record<string, string>).Authorization;
    }
  },
});

// HMAC request signing and secure logging
const signed = createStormFetchClient({
  baseURL: 'https://api.example.com',
  plugins: [
    hmacSigningPlugin({ secret: () => process.env.STORM_SIGNING_SECRET ?? '' }),
    secureLoggerPlugin(),
    securityRateLimitPlugin({ threshold: 5 }),
  ],
});

// TLS / mTLS / certificate pinning in Node
await signed.get('/secure', {
  tls: {
    ca: process.env.API_CA,
    cert: process.env.API_CERT,
    key: process.env.API_KEY,
    certificatePins: ['sha256/base64-pin-here'],
  },
});

Security Capabilities

| Area | StormFetch 1.13.0 | | --- | --- | | Runtime dependencies | No runtime dependency | | HTTP/2 | adapter: 'http2' uses Node's built-in HTTP/2 client | | Deno and Bun | Official fetch-adapter path with runtime detection | | Enterprise proxy bypass | proxy.noProxy plus NO_PROXY/no_proxy normalization | | Redirect credentials | Cross-origin redirects strip sensitive headers by default; beforeRedirect can inspect/block/sign | | DNS rebinding | Optional DNS resolution re-check with CIDR policy | | Cloud metadata SSRF | Metadata hosts blocked by security policy | | Path traversal | baseURL path traversal and absolute URL override guards | | Payload DoS | 10MB secure default caps plus JSON depth/key limits | | JSON parsing | parseReviver, request/response JSON depth/key caps | | Decompression bombs | maxDecompressionRatio plus byte caps | | Socket paths | Socket paths blocked unless explicitly allowlisted | | TLS/mTLS | Node TLS options and certificate pins | | Signing | Built-in HMAC signing plugin | | Secure logging | Built-in secure logger plugin | | CI scanning | zizmor, CodeQL, Semgrep, dependency review, SBOM | | Credential binding | Per-header forwarding policies | | HTTPS guard | Built-in through security.allowHttp: false | | Host allow/block policy | Built-in exact, wildcard, RegExp, or function matching | | Private-network/SSRF guard | Built-in allowPrivateNetwork: false policy | | Debug export safety | history(), toCurl(), and toHAR() redact headers, URL credentials, query secrets, and body token patterns | | XSRF handling | Optional XSRF with same-origin protection by default | | Retry safety | Unsafe methods are not retried unless opted in | | Schema validation | Request/response validator hooks included | | Security process | SECURITY.md, THREATMODEL.md, release checklist, denylist, smoke tests, npm OIDC/provenance publish workflow |

These controls live in the client itself, so teams can make one hardened API client and reuse the same policy in React, React Native, SSR, and Node.

Complete Function Guide

Client creation functions

| Function | Use it for | What user should do | | --- | --- | --- | | createStormFetchClient(options) | Main client for ReactJS, browser, Node.js, SSR, and shared SDKs | Create one configured API client with baseURL, auth token, timeout, cache, retry, plugins | | createReactNativeStormFetchClient(options) | React Native and Expo apps | Use this when app runs on mobile; provide async token storage, network state, and native file saver | | createStormFetchServerClient(options) | SSR/Node server requests | Forward cookies/headers from server request to API calls | | createTypedStormFetchClient<ApiSchema>(options) | Compile-time typed API routes | Define endpoint map once, then get typed response/body inference | | fastApi | Quick experiments and simple demos | Use pre-created default client; prefer custom client in production |

Universal request functions

| Function | What it does | Best use case | | --- | --- | --- | | request(config) | Sends any HTTP request using full config | Dynamic method/url, custom adapters, shared request builders | | fetch(url, config) | Fetch-like API but with StormFetch retry/cache/auth/interceptors | Developers who already know browser fetch | | get(url, config) / GET(url, config) | Reads data | Lists, details, search, cacheable API calls | | post(url, data, config) / POST(...) | Sends create/action requests | Create record, login, command APIs | | put(url, data, config) / PUT(...) | Replaces a full resource | Full update forms | | patch(url, data, config) / PATCH(...) | Updates partial resource fields | Profile edit, status update | | delete(url, config) / DELETE(...) | Removes data | Delete record, revoke token | | head(url, config) / HEAD(...) | Reads only status and headers | File exists, auth check, CDN metadata | | options(url, config) / OPTIONS(...) | Checks API capabilities | CORS/method discovery |

Body and API style helpers

| Function | What it does | Best use case | | --- | --- | --- | | postJson(url, data) | Sends JSON with explicit JSON header | Strict JSON APIs | | postForm(url, data) | Sends application/x-www-form-urlencoded | OAuth, login forms, legacy APIs | | postMultipart(url, data) | Sends multipart/form-data | File/image/document uploads | | graphql(query, variables, config) | Sends GraphQL operation to /graphql or configured endpoint | GraphQL backends | | batch(requests) | Runs many requests in parallel and keeps response order | Dashboard bootstrap, loading many widgets | | poll(url, options) | Repeats GET until stopped or condition matches | Payment status, background job status, live order tracking |

REST and SDK builder functions

| Function | What it does | Best use case | | --- | --- | --- | | resource(basePath) | Creates list, detail, create, replace, update, remove helpers | Standard REST CRUD modules | | contract(definition) | Creates named service functions from endpoint definitions | SDK-like API layer without writing repetitive functions | | generateStormFetchServicesFromOpenApi(document) | Generates TypeScript service source from OpenAPI | Large APIs where services should be generated |

Cache and performance functions

| Function | What it does | Best use case | | --- | --- | --- | | cacheManager.prefetch(url, config) | Loads and stores cache before UI needs it | Faster screen transitions | | dehydrateCache() | Exports current cache snapshot | SSR hydration, app state transfer | | hydrateCache(snapshot) | Restores a cache snapshot | SSR/client boot, offline restore | | cacheKeys() | Lists in-memory cache keys | Debugging and cache inspection | | clearCache(key?) | Clears cache immediately | Logout, forced refresh | | clearCacheAsync(key?) | Clears cache and awaits async storage | React Native AsyncStorage/MMKV wrappers | | invalidateTags(tags) | Removes cache entries by tags | Refresh lists after create/update/delete |

Debugging and DevTools-style functions

| Function | What it does | Best use case | | --- | --- | --- | | history() | Returns recent request history | Debug panel, request inspection | | clearHistory() | Removes stored request history | Reset debug state | | replay(requestId) | Re-runs a previous request | Reproduce API bugs | | toCurl(requestIdOrConfig) | Exports request as curl command | Share issue with backend team | | toHAR() | Exports HAR-like request log | Browser-like network diagnostics | | subscribe(listener) | Listens to request/cache events | Logging, metrics, custom DevTools |

Reliability and policy functions

| Function / option | What it does | Best use case | | --- | --- | --- | | policy(match, config) | Applies defaults to matching routes | Different timeout/retry/cache rules for payments/admin/upload | | retry, retryPolicy, retryDelay | Retries failed requests | Network instability | | dedupe | Shares identical in-flight GET requests | Prevent duplicate list/detail calls | | priority, maxConcurrent, maxConcurrentPerHost | Controls request scheduling | Apps with many parallel requests | | circuitBreaker | Stops hitting failing hosts and optionally returns fallback | Production outage protection | | offlineQueue + flushOfflineQueue() | Queues requests while offline, then flushes later | Mobile/offline friendly workflows |

HTML, XML, scraping, and DOM functions

| Function | What it does | Best use case | | --- | --- | --- | | getHtml(url) | Fetches HTML as text | Static pages, public content | | getXml(url) | Fetches XML as text | RSS feeds, sitemaps, XML APIs | | getDom(url) | Fetches and parses DOM | Browser scraping or custom parser flows | | parseDom(html, mimeType) | Parses HTML/XML string | Reuse parsing without network call | | scrape(url, schema) | Extracts values with CSS selectors | Titles, meta descriptions, links, images | | selectText(document, selector) | Extracts text from first match | DOM helper | | selectAttr(document, selector, attr) | Extracts attribute from first match | href, src, content | | selectAll(document, selector, attr?) | Extracts all matches | Link/image lists | | extractMeta(document) | Reads meta tags into object | SEO/content tools | | extractLinks(document) | Extracts all anchor links | Crawlers | | extractImages(document) | Extracts all image sources | Media extraction | | extractJsonLd(document) | Parses JSON-LD scripts | SEO structured data |

Streaming functions

| Function | What it does | Best use case | | --- | --- | --- | | streamText(url) | Async-iterates text chunks | AI text streams, logs | | streamJson(url) | Reads a streamed JSON response into object | Large JSON endpoint | | streamNdjson(url) | Async-iterates newline-delimited JSON rows | Event/log feeds | | sse(url, options) | Subscribes to Server-Sent Events | Live notifications and server events |

Upload and download functions

| Function | What it does | Best use case | | --- | --- | --- | | createFormData(data) | Converts object to multipart FormData | Upload with fields and files | | createReactNativeFile(uri, name, type) | Creates typed RN file descriptor | Expo/ImagePicker/DocumentPicker upload | | isReactNativeFile(value) | Type guard for RN file descriptor | Validation before upload | | download(url, fileName, config) | Downloads Blob and optionally saves | Reports, invoices, exports | | saveBlob(blob, fileName) | Browser-only Blob save helper | Manual browser save flow |

Testing and mock functions

| Function | What it does | Best use case | | --- | --- | --- | | createMockAdapter(options) | Creates simple mock HTTP adapter | Unit tests and demos | | createMockServer(options) | Creates scenario-aware mock server with request inspection | Storybook, integration tests, empty/error/success states | | createStormFetchError(input) | Creates StormFetch-compatible error | Custom adapters and test failures | | createInterceptorManager() | Creates standalone interceptor manager | Advanced plugin/testing utilities |

Runtime and utility functions

| Function | What it does | Best use case | | --- | --- | --- | | getRuntimeInfo() | Returns detected runtime and capability flags | Debug/support reports | | isBrowser() | Checks browser runtime | Conditional browser behavior | | isReactNative() | Checks React Native runtime | Conditional native behavior | | isNodeLike() | Checks Node runtime excluding RN process shim | SSR/server behavior | | buildQueryString(params) | Serializes query params | Custom URL builders | | replacePathParams(url, params) | Replaces /users/:id placeholders | REST path building | | joinURL(baseURL, url) | Safely joins base and path | Shared service code |

CRUD Example

interface User {
  id: number;
  name: string;
  email: string;
}

interface UserInput {
  name: string;
  email: string;
}

const list = () => api.get<User[]>('/users');
const detail = (id: number) => api.get<User>('/users/:id', { pathParams: { id } });
const create = (input: UserInput) => api.post<User, UserInput>('/users', input);
const replace = (id: number, input: UserInput) =>
  api.put<User, UserInput>('/users/:id', input, { pathParams: { id } });
const update = (id: number, input: Partial<UserInput>) =>
  api.patch<User, Partial<UserInput>>('/users/:id', input, { pathParams: { id } });
const remove = (id: number) => api.delete('/users/:id', { pathParams: { id } });

Typed Endpoint Map

Use createTypedStormFetchClient when routes should infer their response and request body types.

type ApiSchema = {
  '/users': {
    GET: { data: User[] };
    POST: { data: User; body: UserInput };
  };
};

const typedApi = createTypedStormFetchClient<ApiSchema>({
  baseURL: 'https://api.example.com',
});

const users = await typedApi.get('/users');
const created = await typedApi.post('/users', { name: 'Asha', email: '[email protected]' });

Interceptors and Plugins

const removeInterceptor = api.interceptors.request.use((config) => ({
  ...config,
  headers: { ...config.headers, 'X-App-Version': '2.0.0' },
}));

removeInterceptor();
const api = createStormFetchClient({
  plugins: [
    authPlugin(() => getToken()),
    languagePlugin(() => getLanguage()),
    loggerPlugin(),
  ],
});

Retry, Cache, Dedupe, and Cancellation

const controller = new AbortController();

const request = api.get<User[]>('/users', {
  retryPolicy: 'safe',
  retry: 3,
  cache: true,
  cacheTTL: 120_000,
  cacheTags: ['users'],
  staleTime: 30_000,
  staleWhileRevalidate: true,
  dedupe: true,
  signal: controller.signal,
});

controller.abort();

retryUnsafe must be explicitly enabled before POST, PATCH, or DELETE requests are retried.

Advanced Features

Async cache, tags, and stale refresh

const users = await api.get<User[]>('/users', {
  cache: true,
  cacheStorage: asyncStorageLikeStore,
  cacheTTL: 5 * 60_000,
  staleTime: 30_000,
  staleWhileRevalidate: true,
  cacheTags: ['users'],
});

await api.invalidateTags(['users']);

Use this in React Native with AsyncStorage/MMKV-style wrappers, or in web apps where you want tagged invalidation after mutations.

Infinite queries

const feed = useStormInfiniteQuery<Post[], string | undefined>('/feed', {
  initialPageParam: undefined,
  createConfig: (cursor) => ({ params: { cursor }, cache: true, cacheTags: ['feed'] }),
  getNextPageParam: (lastPage) => lastPage.at(-1)?.nextCursor,
});

Mock adapter for tests

import { createMockAdapter, createStormFetchClient } from 'stormfetch';

const api = createStormFetchClient({
  adapter: createMockAdapter({
    routes: [
      { method: 'GET', url: '/users', data: [{ id: 1, name: 'Demo' }] },
    ],
  }),
});

Universal methods, GraphQL, batch, and polling

await api.HEAD('/reports/monthly.pdf');
await api.POST('/users', { name: 'Asha' });

const profile = await api.fetch<User>('/me', { method: 'GET' });

const graph = await api.graphql<{ user: User }>(
  `query User($id: ID!) { user(id: $id) { id name } }`,
  { id: '1' }
);

const [users, stats] = await api.batch<[User[], Stats]>([
  { url: '/users', method: 'GET' },
  { url: '/stats', method: 'GET' },
]);

const poller = api.poll<Job>('/jobs/123', {
  interval: 2_000,
  stopWhen: (response) => response.data.status === 'done',
  onData: (response) => console.log(response.data.status),
});

poller.stop();

HTML, XML, DOM parsing, and scraping

const page = await api.getHtml('/public-page');
const feed = await api.getXml('/feed.xml');

const scraped = await api.scrape('/blog/post', {
  title: 'h1',
  description: 'meta[name="description"]@content',
  links: ['a@href'],
});

In browsers, StormFetch uses native DOMParser. In Node.js or React Native, provide a parser adapter:

const api = createStormFetchClient({
  domParser: {
    parse(html, mimeType) {
      return yourParser(html, mimeType);
    },
  },
});

REST resource builder

const users = api.resource<User, UserInput>('/users');

await users.list();
await users.detail(10);
await users.create({ name: 'Asha' });
await users.update(10, { name: 'Asha S.' });
await users.remove(10);

Advanced StormFetch Features

await api.policy('/payments/**', {
  timeout: 10_000,
  retry: 0,
  headers: { 'X-Scope': 'payments' },
});

await api.cacheManager.prefetch<User[]>('/users', {
  cacheTags: ['users'],
});

const snapshot = await api.dehydrateCache();
await api.hydrateCache(snapshot);

const history = api.history();
const curl = api.toCurl(history[0].id);
const har = api.toHAR();
await api.replay(history[0].id);
const api = createStormFetchClient({
  circuitBreaker: {
    failureThreshold: 3,
    resetTimeout: 30_000,
    fallback: () => ({ cached: true }),
  },
});
for await (const line of api.streamNdjson<{ event: string }>('/events.ndjson')) {
  console.log(line.event);
}

const events = api.sse('/events', {
  onMessage: (message) => console.log(message.event, message.data),
});

events.close();
const sdk = api.contract({
  listUsers: { method: 'GET', url: '/users' },
  createUser: { method: 'POST', url: '/users' },
});

await sdk.listUsers<User[]>();
await sdk.createUser<User, UserInput>({ name: 'Asha' });
import { createMockServer } from 'stormfetch';

const mock = createMockServer({
  scenarios: {
    empty: [{ method: 'GET', url: '/users', data: [] }],
    filled: [{ method: 'GET', url: '/users', data: [{ id: 1 }] }],
  },
});

mock.setScenario('filled');
const api = createStormFetchClient({ adapter: mock.adapter });
console.log(mock.requests());

Uploads

Browser:

const body = api.createFormData({ avatar: file, displayName: 'Pradeep' });
await api.post('/profile/avatar', body);

React Native:

import { createFormData, createReactNativeFile } from 'stormfetch/react-native';

const body = createFormData({
  avatar: createReactNativeFile(image.uri, image.fileName ?? 'avatar.jpg', image.mimeType ?? 'image/jpeg'),
  displayName: 'Pradeep',
});

await api.post('/profile/avatar', body);

Do not manually set the multipart Content-Type; StormFetch removes it so the runtime can add the boundary.

Downloads

// Browser: automatically opens the save flow.
await api.download('/reports/monthly', 'monthly.pdf');

// Any runtime: fetch without auto-saving.
const response = await api.download('/reports/monthly', 'monthly.pdf', {
  autoSave: false,
});

React Native must provide fileSaver at client or request level because the package intentionally does not depend on a particular native filesystem library.

Errors

try {
  await api.get('/users');
} catch (cause) {
  const error = cause as import('stormfetch').StormFetchError;
  console.log(error.status, error.code, error.fieldErrors, error.traceId);
  console.log(error.isNetworkError, error.isTimeoutError, error.isAbortError);
}

Runtime Diagnostics

import { getRuntimeInfo } from 'stormfetch';

console.log(getRuntimeInfo());
// { runtime, isBrowser, isReactNative, isNode, hasFetch, hasXHR, ... }

Important Platform Notes

  • Browser XHR is selected when progress callbacks are requested.
  • Browser XHR is also selected for adapter: 'fetch' when onUploadProgress is supplied, because Fetch does not expose portable upload progress.
  • Node HTTP uploads support native FormData serialization and chunked buffered upload progress.
  • React Native uses fetch; standard fetch does not provide portable upload progress events.
  • Browser localStorage and sessionStorage cache are not available in React Native.
  • React Native auth token providers may be asynchronous.
  • React Native file saving is injected through fileSaver.
  • Node-specific proxy, stream, redirect, and size options use the HTTP adapter.

Development and Publishing

npm install
npm run typecheck
npm run build
npm pack --dry-run

Publish only after the checks pass:

npm publish --access public

Advanced Roadmap

High-value future improvements are listed in FEATURES.md, including async persistent storage for React Native, native progress adapters, query invalidation, pagination hooks, circuit breaking, observability, mocks, and generated clients.

License

MIT