zipfetch
v1.0.2
Published
Ultra-fast, lightweight HTTP client built on native fetch - retries, interceptors, caching, rate limiting
Maintainers
Readme
zipfetch
Ultra-fast, lightweight HTTP client built on native fetch - retries, interceptors, caching, rate limiting
Why zipfetch?
- Tiny: ~5KB minified (vs Axios 29KB, Got 48KB)
- Zero dependencies: Built on native
fetch(Node 18+) - Modern: ESM, TypeScript, async/await
- Powerful: Retries, interceptors, caching, rate limiting
- Familiar API: Similar to Axios, easy to migrate
Installation
npm install zipfetchQuick Start
import zipfetch from 'zipfetch';
// Simple GET request
const { data } = await zipfetch.get('https://api.example.com/users');
// POST with data
const { data: user } = await zipfetch.post('https://api.example.com/users', {
name: 'John',
email: '[email protected]'
});
// With query params
const { data: results } = await zipfetch.get('https://api.example.com/search', {
params: { q: 'typescript', page: 1 }
});Comparison with Alternatives
| Feature | zipfetch | Axios | Got | node-fetch | |---------|----------|-------|-----|------------| | Size (gzip) | ~5KB | 29KB | 48KB | 8KB | | Dependencies | 0 | 8 | 11 | 0 | | Native fetch | Yes | No | No | Polyfill | | Retries | Built-in | No | Yes | No | | Interceptors | Yes | Yes | Hooks | No | | Caching | Built-in | No | Plugin | No | | Rate Limiting | Built-in | No | Plugin | No | | Progress Tracking | Built-in | Yes | Yes | No | | Request Cancellation | Built-in | Yes | Yes | No | | Streaming | Built-in | Yes | Yes | No | | Auth Helpers | Built-in | Yes | No | No | | Deduplication | Built-in | No | No | No | | Concurrency Control | Built-in | No | Plugin | No | | TypeScript | Yes | Yes | Yes | @types | | Node 18+ | Yes | Yes | Yes | Yes | | Browser | Yes | Yes | No | No |
API Reference
Creating an Instance
import { create } from 'zipfetch';
const api = create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Authorization': 'Bearer token'
}
});
const { data } = await api.get('/users');Request Methods
zipfetch.get(url, config?)
zipfetch.post(url, data?, config?)
zipfetch.put(url, data?, config?)
zipfetch.patch(url, data?, config?)
zipfetch.delete(url, config?)
zipfetch.head(url, config?)
zipfetch.options(url, config?)Request Config
interface RequestConfig {
url?: string;
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
baseURL?: string;
headers?: Record<string, string>;
params?: Record<string, string | number | boolean>;
data?: unknown;
timeout?: number;
retry?: {
attempts?: number;
delay?: number;
backoff?: 'linear' | 'exponential';
retryOn?: number[];
};
cache?: boolean;
responseType?: 'json' | 'text' | 'blob' | 'arrayBuffer' | 'stream';
auth?: {
type: 'bearer' | 'basic' | 'custom';
token?: string;
username?: string;
password?: string;
headerValue?: string;
};
onUploadProgress?: (event: ProgressEvent) => void;
onDownloadProgress?: (event: ProgressEvent) => void;
skipDedupe?: boolean;
}Instance Config
interface ZipFetchConfig {
baseURL?: string;
timeout?: number;
headers?: Record<string, string>;
retry?: RetryConfig;
cache?: CacheConfig;
rateLimit?: RateLimitConfig;
concurrency?: number; // Max concurrent requests
dedupe?: boolean; // Deduplicate identical in-flight requests
auth?: AuthConfig; // Default auth for all requests
}Response Object
interface ZipResponse<T> {
data: T;
status: number;
statusText: string;
headers: Headers;
config: RequestConfig;
duration: number; // Request duration in milliseconds
}Features
Automatic Retries
import { create } from 'zipfetch';
const api = create({
retry: {
attempts: 3, // Retry up to 3 times
delay: 1000, // Start with 1 second delay
backoff: 'exponential', // 1s, 2s, 4s
retryOn: [500, 502, 503] // Retry on these status codes
}
});
// Will automatically retry on failure
const { data } = await api.get('/flaky-endpoint');Interceptors
import zipfetch from 'zipfetch';
// Request interceptor
zipfetch.interceptors.request.use((config) => {
config.headers = {
...config.headers,
'X-Request-ID': crypto.randomUUID()
};
return config;
});
// Response interceptor
zipfetch.interceptors.response.use((response) => {
console.log(`Response: ${response.status}`);
return response;
});
// Error interceptor
zipfetch.interceptors.error.use((error) => {
if (error.status === 401) {
// Handle unauthorized
window.location.href = '/login';
}
return error;
});Response Caching
import { create } from 'zipfetch';
const api = create({
cache: {
enabled: true,
ttl: 60000, // Cache for 60 seconds
methods: ['GET'] // Only cache GET requests
}
});
// First request hits the network
await api.get('/users');
// Second request returns cached response
await api.get('/users');
// Clear cache manually
api.clearCache();Rate Limiting
import { create } from 'zipfetch';
const api = create({
rateLimit: {
requests: 10, // Max 10 requests
interval: 1000 // Per second
}
});
// Requests will automatically queue if rate limit is exceeded
for (let i = 0; i < 20; i++) {
api.get('/endpoint'); // Will spread across 2 seconds
}Timeout
import zipfetch from 'zipfetch';
try {
await zipfetch.get('/slow-endpoint', {
timeout: 5000 // 5 second timeout
});
} catch (error) {
if (error.code === 'TIMEOUT') {
console.log('Request timed out');
}
}Authentication
Built-in auth helpers for Bearer, Basic, and custom authentication:
import { create } from 'zipfetch';
// Bearer token authentication
const api = create({
baseURL: 'https://api.example.com',
auth: {
type: 'bearer',
token: 'your-jwt-token'
}
});
// Basic authentication
const api = create({
auth: {
type: 'basic',
username: 'user',
password: 'pass'
}
});
// Custom header value
const api = create({
auth: {
type: 'custom',
headerValue: 'ApiKey your-api-key'
}
});
// Per-request auth override
await api.get('/protected', {
auth: { type: 'bearer', token: 'different-token' }
});Progress Tracking
Track upload and download progress for large files:
import zipfetch from 'zipfetch';
// Upload progress
await zipfetch.post('/upload', largeFile, {
onUploadProgress: (event) => {
console.log(`Uploaded: ${event.percent.toFixed(1)}%`);
console.log(`${event.loaded} / ${event.total} bytes`);
}
});
// Download progress
await zipfetch.get('/large-file', {
onDownloadProgress: (event) => {
console.log(`Downloaded: ${event.percent.toFixed(1)}%`);
}
});Request Cancellation
Cancel in-flight requests using CancelToken:
import { ZipFetch } from 'zipfetch';
// Create a cancel token
const source = ZipFetch.CancelToken.source();
// Make request with cancel token
const promise = zipfetch.get('/slow-endpoint', {
signal: source.token.controller.signal
});
// Cancel the request
source.cancel('User cancelled');
// Check if cancelled
if (source.token.isCancelled) {
console.log('Request was cancelled:', source.token.reason);
}Streaming Responses
Get the raw response stream for large files or real-time data:
import zipfetch from 'zipfetch';
const { data: stream } = await zipfetch.get('/stream', {
responseType: 'stream'
});
// Process the ReadableStream
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('Received chunk:', value);
}Request Deduplication
Automatically deduplicate identical in-flight GET requests:
import { create } from 'zipfetch';
const api = create({
dedupe: true // Enabled by default
});
// These two calls will result in only ONE network request
const [res1, res2] = await Promise.all([
api.get('/users'),
api.get('/users') // Returns same promise as first call
]);
// Disable for specific requests
await api.get('/users', { skipDedupe: true });Concurrency Control
Limit the number of concurrent requests:
import { create } from 'zipfetch';
const api = create({
concurrency: 5 // Max 5 concurrent requests
});
// Only 5 requests will be in-flight at once
// Others will queue and execute as slots become available
const promises = urls.map(url => api.get(url));
await Promise.all(promises);Error Handling
import zipfetch, { ZipError } from 'zipfetch';
try {
await zipfetch.get('/not-found');
} catch (error) {
if (error instanceof ZipError) {
console.log(error.status); // 404
console.log(error.response); // Full response object
console.log(error.config); // Request config
console.log(error.code); // 'TIMEOUT' | 'NETWORK' | 'ABORT' | undefined
}
}FormData & File Uploads
import zipfetch from 'zipfetch';
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('name', 'My File');
const { data } = await zipfetch.post('/upload', formData);
// Content-Type is automatically set to multipart/form-dataMigration from Axios
// Axios
import axios from 'axios';
const api = axios.create({ baseURL: 'https://api.example.com' });
const { data } = await api.get('/users');
// zipfetch
import { create } from 'zipfetch';
const api = create({ baseURL: 'https://api.example.com' });
const { data } = await api.get('/users');Migration from node-fetch
// node-fetch
import fetch from 'node-fetch';
const response = await fetch('https://api.example.com/users');
const data = await response.json();
// zipfetch
import zipfetch from 'zipfetch';
const { data } = await zipfetch.get('https://api.example.com/users');TypeScript
Full TypeScript support with generics:
import zipfetch from 'zipfetch';
interface User {
id: number;
name: string;
email: string;
}
// Type-safe response
const { data } = await zipfetch.get<User[]>('/users');
data.forEach(user => console.log(user.name));
// Type-safe post
const { data: newUser } = await zipfetch.post<User>('/users', {
name: 'John',
email: '[email protected]'
});Browser Support
Works in all modern browsers that support native fetch:
- Chrome 42+
- Firefox 39+
- Safari 10.1+
- Edge 14+
For older browsers, use a fetch polyfill.
Node.js Support
Requires Node.js 18+ (native fetch support).
Support
If zipfetch saved you time and made HTTP requests easier, consider supporting the project:
- ⭐ Star the repo—it helps others discover this tool
- 📢 Share with your team or on social media
- 🐛 Report bugs or suggest features
- ☕ Buy me a coffee if you'd like to support development
License
MIT
Made for speed and simplicity
