@camozdevelopment/voltfetch
v0.2.0
Published
A secure, lightweight, edge-ready fetch client for TypeScript—retries, timeouts, caching, axios-friendly API, zero runtime dependencies.
Maintainers
Readme
VoltFetch
A secure, lightweight, edge-ready HTTP client for TypeScript—built on fetch, with axios-friendly ergonomics and zero runtime dependencies.
npm install @camozdevelopment/voltfetchRequires Node.js ≥ 18 (or any environment with global fetch). ESM-only.
Why VoltFetch over Axios?
| | Axios | VoltFetch |
|---|-----------|---------------|
| Transport | XHR / http adapters, large surface | Native fetch everywhere |
| Bundle / deps | Multiple dependencies | Zero runtime dependencies |
| Edge & Workers | Often awkward or extra config | Same code on Node, browser, Bun, CF Workers, Vercel Edge |
| Security | DIY for SSRF-style controls | allowedDomains, blockedDomains, blockPrivateIPs, redirect auth stripping, maxResponseSize |
| Retries / timeout | Optional plugins or manual | Built-in retries with backoff + Retry-After, timeouts without timer leaks |
| Caching | External | Optional in-memory GET cache + in-flight dedupe |
| Typing | Mature | First-class generics + stable VoltFetchError codes |
| Familiar API | axios.create, interceptors | createClient, interceptors, params / data, paramsSerializer |
Axios is excellent for many stacks. VoltFetch is for teams that want fetch semantics, smaller installs, edge-native behavior, and explicit outbound URL controls without maintaining a parallel HTTP stack.
Quick start
import { createClient, VoltFetchError } from "@camozdevelopment/voltfetch";
const api = createClient({
baseURL: "https://api.example.com",
timeout: 5000,
retries: 2,
retryDelay: 300,
headers: { Accept: "application/json" },
});
const user = await api.get<User>("/users/me");
await api.post("/posts", { title: "Hello" });Axios-style options
const api = createClient({
baseURL: "https://api.example.com",
paramsSerializer: (q) => new URLSearchParams(q as Record<string, string>).toString(),
jsonStripUndefined: true,
interceptors: {
request: [(ctx) => { /* mutate ctx.init.headers */ }],
},
});
// params merges after query; same keys → params wins
await api.get("/search", { query: { q: "a" }, params: { page: 2 } });
// data is an alias for body
await api.request({ url: "/items", method: "POST", data: { name: "x" } });Custom fetch (tests, proxies, runtimes)
const api = createClient({
baseURL: "https://api.example.com",
fetch: myFetchImplementation,
});Build a URL without requesting
import { buildRequestUrl } from "@camozdevelopment/voltfetch";
const href = buildRequestUrl("https://api.example.com", "/users", {
query: { role: "admin" },
});Features
| Feature | Notes |
|--------|--------|
| Methods | get, post, put, patch, delete, head, options, request |
| Body / query | data, params, paramsSerializer, jsonStripUndefined |
| Parsing | parseAs: json | text | blob | arrayBuffer | formData | raw |
| Hooks & interceptors | hooks + interceptors (axios-like naming) |
| Timeouts | AbortController; cleaned up on completion |
| Retries | Extra attempts after the first; backoff + jitter; Retry-After |
| Cache / dedupe | Optional memory cache + in-flight GET coalescing |
| Security | Domain allow/block lists, literal private IP block, redirect hardening, size limits |
| fetch injection | Client-level and per-request override |
Upload progress is not faked: the Fetch standard does not expose it cross-runtime. Use ReadableStream bodies where supported.
Configuration (summary)
| Option | Purpose |
|--------|---------|
| baseURL | Required for relative URLs |
| timeout | Ms; 0 = off |
| retries | Extra attempts (not including the first) |
| retryDelay / retryOn | Backoff + custom retry policy (number[] or function) |
| cache / cacheTTL / dedupe | GET caching and deduplication |
| allowedDomains / blockedDomains / blockPrivateIPs | Outbound URL policy |
| maxResponseSize / maxRedirects / followRedirects | Safety limits |
| throwOnHTTPError / validateStatus | HTTP error behavior |
| fetch | Custom fetch implementation |
| interceptors | { request, response, error } arrays (run before hooks) |
Full tables and hook examples: see Documentation site (npm run site:dev) or source src/types.ts.
Errors
VoltFetchError exposes code (TIMEOUT, NETWORK_ERROR, SECURITY_ERROR, HTTP_ERROR, ABORT, RESPONSE_SIZE_EXCEEDED, REDIRECT_ERROR, UNKNOWN), status, data, response, and flags like isHTTPError. Use VoltFetchError.fromUnknown(err) to normalize unknown errors.
Security
See SECURITY.md for reporting and scope. VoltFetch does not resolve DNS for outbound URLs; literal-IP and host-pattern checks complement—but do not replace—egress controls for high-risk SSRF scenarios.
Website
npm run site:dev # local dev
npm run site:build # → website/dist/Exports
import voltfetch, {
createClient,
VoltFetchError,
buildRequestUrl,
mergeQueryLayers,
stringifyQuery,
stripUndefinedDeep,
createMemoryCache,
MemoryCacheStore,
buildAuthHeaders,
createAuthHeaders,
mergeHooks,
defaultRetryOn,
parseRetryAfter,
resolveRetryDecision,
assertUrlSafe,
isPrivateOrLocalHost,
} from "@camozdevelopment/voltfetch";Contributing & changelog
License
MIT — see LICENSE.
