- Added cache strategies: NetworkFirst, CacheFirst, StaleWhileRevalidate, NetworkOnly, and CacheOnly. - Introduced InterceptorManager for managing request, response, and error interceptors. - Developed RetryManager for handling request retries with customizable backoff strategies. - Implemented RequestDeduplicator to prevent simultaneous identical requests. - Created timeout utilities for handling request timeouts. - Enhanced WebrequestClient to support global interceptors, caching, and retry logic. - Added convenience methods for common HTTP methods (GET, POST, PUT, DELETE) with JSON handling. - Established a fetch-compatible webrequest function for seamless integration. - Defined core type structures for caching, retry options, interceptors, and web request configurations.
8.7 KiB
8.7 KiB
Migration Guide: v3 → v4
Overview
Version 4.0 is a complete modernization of @push.rocks/webrequest, bringing it in line with modern web standards while maintaining backward compatibility through a deprecated API layer.
What's New in v4
Core Improvements
- Fetch-Compatible API: Drop-in replacement for native
fetch()with enhanced features - Intelligent HTTP Caching: Respects
Cache-Control,ETag,Last-Modified, andExpiresheaders - Multiple Cache Strategies: network-first, cache-first, stale-while-revalidate, network-only, cache-only
- Advanced Retry System: Configurable retry with exponential/linear/constant backoff
- Request/Response Interceptors: Middleware pattern for transforming requests and responses
- Request Deduplication: Automatically deduplicate simultaneous identical requests
- TypeScript Generics: Type-safe response parsing with
webrequest.getJson<T>() - Better Fault Tolerance: Enhanced multi-endpoint fallback with retry strategies
Bug Fixes
- Fixed:
deleteJson()now correctly usesDELETEmethod instead ofGET
Migration Path
Option 1: Quick Migration (Recommended)
The v3 WebRequest class is still available but marked as deprecated. Your existing code will continue to work:
// This still works in v4 (with deprecation warnings)
import { WebRequest } from '@push.rocks/webrequest';
const client = new WebRequest({ logging: true });
const data = await client.getJson('https://api.example.com/data', true);
Option 2: Migrate to New API
Basic Fetch-Compatible Usage
// v3
import { WebRequest } from '@push.rocks/webrequest';
const client = new WebRequest();
const response = await client.request('https://api.example.com/data', {
method: 'GET'
});
const data = await response.json();
// v4 - Fetch-compatible
import { webrequest } from '@push.rocks/webrequest';
const response = await webrequest('https://api.example.com/data');
const data = await response.json();
JSON Convenience Methods
// v3
const client = new WebRequest();
const data = await client.getJson('https://api.example.com/data', true);
// v4 - Function API
import { webrequest } from '@push.rocks/webrequest';
const data = await webrequest.getJson('https://api.example.com/data', {
cacheStrategy: 'cache-first'
});
// v4 - Client API (similar to v3)
import { WebrequestClient } from '@push.rocks/webrequest';
const client = new WebrequestClient({ logging: true });
const data = await client.getJson('https://api.example.com/data', {
cacheStrategy: 'cache-first'
});
Feature Comparison
Caching
// v3 - Boolean flag
const data = await client.getJson(url, true); // useCache = true
// v4 - Explicit strategies
const data = await webrequest.getJson(url, {
cacheStrategy: 'cache-first',
cacheMaxAge: 60000 // 60 seconds
});
// v4 - HTTP header-based caching (automatic)
const data = await webrequest.getJson(url, {
cacheStrategy: 'network-first' // Respects Cache-Control headers
});
// v4 - Stale-while-revalidate
const data = await webrequest.getJson(url, {
cacheStrategy: 'stale-while-revalidate' // Return cache, update in background
});
Multi-Endpoint Fallback
// v3
const client = new WebRequest();
const response = await client.requestMultiEndpoint(
['https://api1.example.com/data', 'https://api2.example.com/data'],
{ method: 'GET' }
);
// v4
import { webrequest } from '@push.rocks/webrequest';
const response = await webrequest('https://api1.example.com/data', {
fallbackUrls: ['https://api2.example.com/data'],
retry: {
maxAttempts: 3,
backoff: 'exponential'
}
});
Timeout
// v3
const response = await client.request(url, {
method: 'GET',
timeoutMs: 30000
});
// v4
const response = await webrequest(url, {
timeout: 30000 // milliseconds
});
New Features in v4
Retry Strategies
import { webrequest } from '@push.rocks/webrequest';
const response = await webrequest('https://api.example.com/data', {
retry: {
maxAttempts: 3,
backoff: 'exponential', // or 'linear', 'constant'
initialDelay: 1000,
maxDelay: 30000,
retryOn: [408, 429, 500, 502, 503, 504], // Status codes to retry
onRetry: (attempt, error, nextDelay) => {
console.log(`Retry attempt ${attempt}, waiting ${nextDelay}ms`);
}
}
});
Request/Response Interceptors
import { webrequest } from '@push.rocks/webrequest';
// Add global request interceptor
webrequest.addRequestInterceptor((request) => {
// Add auth header
const headers = new Headers(request.headers);
headers.set('Authorization', `Bearer ${getToken()}`);
return new Request(request, { headers });
});
// Add global response interceptor
webrequest.addResponseInterceptor((response) => {
console.log(`Response: ${response.status} ${response.url}`);
return response;
});
// Per-request interceptors
const response = await webrequest('https://api.example.com/data', {
interceptors: {
request: [(req) => {
console.log('Sending request:', req.url);
return req;
}],
response: [(res) => {
console.log('Received response:', res.status);
return res;
}]
}
});
Request Deduplication
import { webrequest } from '@push.rocks/webrequest';
// Multiple simultaneous identical requests will be deduplicated
const [res1, res2, res3] = await Promise.all([
webrequest('https://api.example.com/data', { deduplicate: true }),
webrequest('https://api.example.com/data', { deduplicate: true }),
webrequest('https://api.example.com/data', { deduplicate: true }),
]);
// Only one actual network request is made
TypeScript Generics
import { webrequest } from '@push.rocks/webrequest';
interface User {
id: number;
name: string;
email: string;
}
// Type-safe response parsing
const user = await webrequest.getJson<User>('https://api.example.com/user/1');
// user is typed as User
const users = await webrequest.getJson<User[]>('https://api.example.com/users');
// users is typed as User[]
Custom Cache Keys
import { webrequest } from '@push.rocks/webrequest';
const response = await webrequest('https://api.example.com/search?q=test', {
cacheStrategy: 'cache-first',
cacheKey: (request) => {
// Custom cache key based on URL without query params
const url = new URL(request.url);
return `search:${url.searchParams.get('q')}`;
}
});
Advanced Client Configuration
import { WebrequestClient } from '@push.rocks/webrequest';
// Create a client with default options
const apiClient = new WebrequestClient({
logging: true,
timeout: 30000,
cacheStrategy: 'network-first',
retry: {
maxAttempts: 3,
backoff: 'exponential'
}
});
// Add global interceptors
apiClient.addRequestInterceptor((request) => {
const headers = new Headers(request.headers);
headers.set('X-API-Key', process.env.API_KEY);
return new Request(request, { headers });
});
// All requests through this client use the configured defaults
const data = await apiClient.getJson('https://api.example.com/data');
Breaking Changes
1. Cache API Changes
Before (v3):
await client.getJson(url, true); // Boolean for cache
After (v4):
await webrequest.getJson(url, {
cacheStrategy: 'cache-first' // Explicit strategy
});
2. Multi-Endpoint API
Before (v3):
await client.requestMultiEndpoint([url1, url2], options);
After (v4):
await webrequest(url1, {
fallbackUrls: [url2],
retry: true
});
3. Constructor Options
Before (v3):
const client = new WebRequest({ logging: true });
After (v4):
// Function API (no constructor)
import { webrequest } from '@push.rocks/webrequest';
// Or client API
import { WebrequestClient } from '@push.rocks/webrequest';
const client = new WebrequestClient({ logging: true });
Deprecation Timeline
- v4.0:
WebRequestclass marked as deprecated but fully functional - v4.x: Continued support with deprecation warnings
- v5.0:
WebRequestclass will be removed
Recommendation
We strongly recommend migrating to the new API to take advantage of:
- Standards-aligned fetch-compatible interface
- Intelligent HTTP caching with header support
- Advanced retry and fault tolerance
- Request/response interceptors
- Better TypeScript support
- Request deduplication
The migration is straightforward, and the new API is more powerful and flexible while being simpler to use.
Need Help?
- Check the updated README.md for comprehensive examples
- Report issues at: https://code.foss.global/push.rocks/webrequest/issues