A lightweight, production-ready HTTP client for JavaScript and TypeScript. Built with simplicity in mind, FetchPlus provides automatic retries, timeout handling, and structured error management—everything you need for reliable API communication without the overhead.
Making HTTP requests shouldn't be complicated. The native fetch API is great, but it lacks essential features that real-world applications need: automatic retries when networks fail, timeouts for hanging requests, and structured error handling that actually tells you what went wrong.
FetchPlus wraps the native fetch API with these critical features built-in, while keeping the implementation lean and understandable. It includes the essential features that actually matter for production applications without unnecessary bloat.
- Lightweight & Dependency-Free - Minimal overhead with zero external dependencies. Pure JavaScript you can audit and understand.
- Automatic Retries - Failing requests are automatically retried with intelligent exponential backoff strategy. Failed 5xx errors and network issues get retried; 4xx errors fail fast.
- Built-in Timeout Protection - Prevent requests from hanging indefinitely. Set per-request timeouts and get predictable error handling.
- Structured Error Objects - Stop guessing what went wrong. FetchPlus errors include HTTP status, URL, method, response data, and the original error for debugging.
- Full TypeScript Support - Complete type definitions included. Get IDE autocomplete and type safety from the start.
- Simple, Familiar API - The same methods you know:
get(),post(),put(),delete(),patch(). No learning curve. - Promise-Based - Built for async/await. Works seamlessly with modern JavaScript patterns.
- Works Everywhere - Node.js 18+ and modern browsers (ES2022+).
npm install @corex24/fetchplusor with yarn:
yarn add @corex24/fetchplusor with pnpm:
pnpm add @corex24/fetchplusThe simplest possible example:
import { FetchPlus } from 'fetchplus';
// Make a GET request
const user = await FetchPlus.get('https://api.example.com/users/1');
console.log(user);That's it. No configuration, no setup. FetchPlus handles the rest.
const users = await FetchPlus.get('https://api.example.com/users', {
query: {
page: 1,
limit: 20,
sort: 'name'
}
});
// Automatically builds: https://api.example.com/users?page=1&limit=20&sort=nameconst newUser = await FetchPlus.post('https://api.example.com/users', {
body: {
name: 'John Doe',
email: 'john@example.com',
role: 'admin'
}
});// Abort if the request takes longer than 5 seconds
const data = await FetchPlus.get('https://api.example.com/data', {
timeout: 5000
});// Retry up to 3 times if the request fails
const response = await FetchPlus.get('https://unstable-api.example.com/data', {
retries: 3
});
// Retry delays follow exponential backoff:
// 1st failure: wait 300ms, retry
// 2nd failure: wait 600ms, retry
// 3rd failure: wait 1200ms, retry
// All failures: throw errorconst response = await FetchPlus.get('https://api.example.com/protected', {
headers: {
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIs...',
'X-API-Key': 'your-api-key',
'Accept': 'application/json'
}
});// Update a resource
const updated = await FetchPlus.put('https://api.example.com/users/1', {
body: {
name: 'Jane Doe',
email: 'jane@example.com'
}
});
// Partial update
const patched = await FetchPlus.patch('https://api.example.com/users/1', {
body: {
email: 'newemail@example.com'
}
});await FetchPlus.delete('https://api.example.com/users/1');Fetch data with a GET request.
Parameters:
url(string, required) - The URL to requestconfig(object, optional) - Request configuration
Returns: Promise resolving to response data
Example:
const data = await FetchPlus.get('https://api.example.com/data', {
query: { id: 123 },
timeout: 5000
});Submit data with a POST request.
Parameters:
url(string, required) - The URL to requestconfig(object, optional) - Request configuration (usebodyproperty for data)
Returns: Promise resolving to response data
Example:
const response = await FetchPlus.post('https://api.example.com/users', {
body: { name: 'John', email: 'john@example.com' },
headers: { 'Content-Type': 'application/json' }
});Replace a resource with a PUT request.
Parameters: Same as POST
Returns: Promise resolving to response data
Partially update a resource with a PATCH request.
Parameters: Same as POST
Returns: Promise resolving to response data
Delete a resource with a DELETE request.
Parameters:
url(string, required) - The URL to requestconfig(object, optional) - Request configuration
Returns: Promise resolving to response data
Make a request with a custom HTTP method.
Parameters:
method(string, required) - HTTP method (e.g., 'GET', 'POST', 'CUSTOM')url(string, required) - The URL to requestconfig(object, optional) - Request configuration
Returns: Promise resolving to response data
Example:
const response = await FetchPlus.request('CUSTOM', 'https://api.example.com/action', {
body: { action: 'deploy' }
});All request methods accept a configuration object with these options:
{
// Base URL prepended to the request URL
baseURL: 'https://api.example.com',
// Custom HTTP headers
headers: {
'Authorization': 'Bearer token',
'X-Custom-Header': 'value'
},
// Query parameters (automatically URL encoded)
query: {
page: 1,
limit: 20
},
// Request body (for POST, PUT, PATCH)
body: {
name: 'John',
email: 'john@example.com'
},
// Request timeout in milliseconds
// If exceeded, request is aborted and error is thrown
timeout: 5000,
// Number of times to retry on failure
// Retries on 5xx errors and network failures
// Does NOT retry on 4xx errors (fail fast)
retries: 3
}FetchPlus provides detailed error information to help you debug issues:
import { FetchPlus } from '@corex24/fetchplus';
import { FetchPlusError } from '@corex24/fetchplus/error';
try {
const user = await FetchPlus.get('https://api.example.com/invalid');
} catch (error) {
// Check if it's a FetchPlus error
if (error instanceof FetchPlusError) {
console.log('HTTP Status:', error.status); // e.g., 404
console.log('Error Message:', error.message); // e.g., "Not Found"
console.log('Request URL:', error.url); // The URL that was requested
console.log('HTTP Method:', error.method); // GET, POST, etc.
console.log('Response Data:', error.data); // Server response body
console.log('Error Code:', error.code); // Internal error code
// Handle specific status codes
if (error.status === 404) {
console.log('Resource not found');
} else if (error.status === 401) {
console.log('Unauthorized - redirect to login');
} else if (error.status >= 500) {
console.log('Server error - might retry');
}
} else {
// Non-HTTP errors (network issues, etc.)
console.error('Network error:', error.message);
}
}{
name: 'FetchPlusError',
message: string, // Human-readable error message
status: number, // HTTP status code (e.g., 404, 500)
url: string, // URL that was requested
method: string, // HTTP method (GET, POST, etc.)
data: any, // Response body from server
code: string, // Error code for categorization
cause: unknown // Original error (if available)
}FetchPlus uses intelligent retry logic:
WILL RETRY (automatically):
- 5xx Server Errors (500, 502, 503, 504, etc.)
- Network Timeouts
- Connection Failures
- AbortError (timeout or manual abort)
WILL NOT RETRY (fail immediately):
- 4xx Client Errors (400, 401, 403, 404, etc.)
- Invalid JSON responses
- Other application-level errors
This strategy ensures you don't waste time retrying requests that will never succeed, while automatically handling transient failures.
// Configure a base URL for all requests
const apiClient = (url, config = {}) =>
FetchPlus.get(url, {
baseURL: 'https://api.example.com',
...config
});
// Usage
const user = await apiClient('/users/1');
// Requests: https://api.example.com/users/1class APIClient {
constructor(baseURL, token) {
this.baseURL = baseURL;
this.token = token;
}
async request(method, path, data = null) {
return FetchPlus.request(method, path, {
baseURL: this.baseURL,
body: data,
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
},
retries: 2,
timeout: 10000
});
}
getUser(id) {
return this.request('GET', `/users/${id}`);
}
createUser(userData) {
return this.request('POST', '/users', userData);
}
updateUser(id, userData) {
return this.request('PUT', `/users/${id}`, userData);
}
deleteUser(id) {
return this.request('DELETE', `/users/${id}`);
}
}
// Usage
const api = new APIClient('https://api.example.com', 'your-auth-token');
const user = await api.getUser(1);
const newUser = await api.createUser({ name: 'John', email: 'john@example.com' });// Fetch multiple resources in parallel
const [users, posts, comments] = await Promise.all([
FetchPlus.get('https://api.example.com/users'),
FetchPlus.get('https://api.example.com/posts'),
FetchPlus.get('https://api.example.com/comments')
]);FetchPlus is perfect for:
- ✅ Small to medium-sized projects
- ✅ REST API consumption
- ✅ Server-side rendering (Next.js, Nuxt, etc.)
- ✅ Microservices and backend applications
- ✅ When you want to keep dependencies minimal
- ✅ When you want to understand your dependencies
- ✅ When you need retries and timeouts, but not interceptors
Consider other libraries if you need:
- ❌ Request/Response interceptors
- ❌ Response caching
- ❌ File streaming or uploads
- ❌ GraphQL-specific features
- ❌ Complex middleware pipelines
FetchPlus is built for developers who want a simple, lightweight HTTP client without bloat. It gives you the essentials—retries, timeouts, and error handling—without forcing you to carry features you don't need.
If you need advanced features like interceptors or streaming, other libraries exist for that. But for straightforward API calls? FetchPlus does it right.
- Lightweight - Minimal bundle footprint with zero external dependencies
- Zero Dependencies - No external packages to install or maintain
- Fast - Minimal overhead, direct fetch wrapper
- Optimized - Respects your bundle size and keeps applications lean
- Node.js 18+
- Modern Browsers (ES2022+)
- Chrome 51+
- Firefox 54+
- Safari 10.1+
- Edge 15+
FetchPlus is fully tested with 54 unit tests and real API integration tests.
npm run test # Run unit tests
npm run test:api # Run real API testsnpm run build # Compile TypeScript
npm run dev # Watch modeMIT © Corex Anthony
Contributions, issues, and feature requests are welcome! Please feel free to check the issues page.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Initial release
- HTTP methods: GET, POST, PUT, DELETE, PATCH
- Automatic retry with exponential backoff
- Request timeout support
- Structured error handling
- Full TypeScript support
- 54 unit tests with 100% passing
- Real API integration tests
If you have questions or need help:
- Check the examples directory for usage patterns
- Review the README for API documentation
- Open an issue on GitHub for bugs or feature requests
FetchPlus is maintained with care for developers who value simplicity, performance, and reliability.