create-request v0.0.1-beta.2
create-request
create-request
is a modern TypeScript library that transforms how you make API calls. Built as an elegant wrapper around the native Fetch API, it provides a chainable, fluent interface that dramatically reduces boilerplate while adding powerful features like automatic retries, timeout handling, and comprehensive error management.
With first-class TypeScript support, built-in response parsing, and intuitive authentication helpers, create-request
makes your API code more readable, more reliable, and fully type-safeβall with zero dependencies and a tiny footprint.
Why create-request?
You've probably been here before: writing boilerplate fetch code, handling retries manually, struggling with timeouts, or wrestling with TypeScript types for your API responses. create-request
solves all of that with an elegant, chainable API:
// Instead of verbose fetch code with manual JSON parsing, error handling, etc.
// You get a clean, intuitive API that just works
const users = await create.get()
.withTimeout(5000)
.withRetries(3)
.withBearerToken('your-token')
.sendTo('https://api.example.com/users')
.json<User[]>(); // Fully typed response!
π Designed for Developer Happiness
- Write Less Code - Accomplish in one line what might take dozens with raw fetch
- Fewer Bugs - Strong typing and smart defaults prevent common API bugs
- Self-Documenting - Chainable API clearly expresses intent for easier code reviews
- Universal - Works the same in browser and Node.js environments
Overview
create-request
provides a chainable, fluent API for making HTTP requests with built-in solutions for the most common API challenges. It's designed for developers who value clean code, type safety, and robust error handling.
Features
- π Fully Typed - Complete TypeScript support with accurate type definitions
- π Chainable API - Fluent interface for building requests
- β±οΈ Timeout Support - Set timeouts for any request (no more hanging requests!)
- π Automatic Retries - Smart retry mechanism with customizable strategies
- π Auth Helpers - One-liner methods for common auth patterns
- π§© Query Parameter Management - Easy query parameter handling with arrays support
- π¦ Response Transformations - Parse responses as JSON, text, blob, etc.
- π« Robust Error Handling - Detailed error information with custom error class
- π Zero Dependencies - Built on the native Fetch API with no external baggage
- π Universal - Works exactly the same in all modern browsers and Node.js
- π§ Smart Content-Type - Automatic content type detection for request bodies
- π·οΈ Type Safety - Enums for all string constants to prevent typos
Installation
npm install create-request
Or with yarn:
yarn add create-request
See the Difference: How create-request Transforms API Calls
1. Simplified Error Handling
create.get()
.sendTo('https://api.example.com/data')
.json()
.then(data => {
// Process successful response
console.log(data);
})
.catch(error => {
if (error instanceof RequestError) {
// Rich error details that fetch doesn't provide:
console.log(`${error.status} error from ${error.method} ${error.url}`);
console.log(`Was it a timeout? ${error.timeoutError}`);
// Original error data still available:
console.log(error.response);
}
});
2. Type-Safe API Responses
interface User {
id: number;
name: string;
email: string;
}
// TypeScript knows exactly what you're getting back!
const users = await create.get()
.sendTo('https://api.example.com/users')
.json<User[]>();
// No more type casting or guessing - your IDE autocomplete works perfectly
users.forEach(user => console.log(user.name));
3. Request Retries Made Simple
// Without create-request: Pages of manual retry logic with delay calculations
// With create-request: One line
const data = await create.get()
.withRetries(3)
.onRetry(({ retryCount, error }) => {
console.log(`Retry ${retryCount} after ${error.message}`);
})
.sendTo('https://api.example.com/flaky-endpoint')
.json();
Basic Usage
import create from 'create-request';
// Simple GET request with chained response parsing
const users = await create.get()
.withTimeout(5000)
.sendTo('https://api.example.com/users')
.json(); // Parse response directly
// POST request with JSON body
const newUser = await create.post()
.withBody({ name: 'Daniel Amenou', email: 'daniel@example.com' })
.withBearerToken('your-token-here')
.sendTo('https://api.example.com/users')
.json();
API Reference
Request Creation
Create requests using the appropriate factory method:
// Methods without body
create.get()
create.del()
create.head()
create.options()
// Methods with body
create.put()
create.post()
create.patch()
Request Configuration
All requests support these configuration methods:
Common Settings
import { RequestPriority, CredentialsPolicy, RequestMode, RedirectMode } from 'create-request';
// Set request headers
.withHeaders({ 'X-API-Key': 'abc123' })
// Set cookies
.withCookies({
sessionId: 'abc123',
preference: 'darkMode',
region: 'us-east'
})
// Set a single content type header
.withContentType('application/json')
// Set request timeout in milliseconds
.withTimeout(5000)
// Configure retry behavior
.withRetries(3)
.onRetry(({ retryCount, error }) => {
console.log(`Retry ${retryCount} after error: ${error.message}`);
})
// Use an external abort controller
.withAbortController(myAbortController)
// Set credentials policy - uses same-origin by default
.withCredentials(CredentialsPolicy.INCLUDE)
// Set redirect behavior
.withRedirect(RedirectMode.FOLLOW)
// Set request mode
.withMode(RequestMode.CORS)
// Set referrer
.withReferrer('https://example.com')
// Set keepalive flag (allows request to outlive the page)
.withKeepAlive(true)
// Set request priority
.withPriority(RequestPriority.HIGH)
Query Parameters
// Add multiple query parameters
.withQueryParams({
search: 'keyword',
page: 1,
filters: ['active', 'verified']
})
// Add a single query parameter
.withQueryParam('sort', 'desc')
Authentication
// Basic authentication
.withBasicAuth('username', 'password')
// Bearer token
.withBearerToken('your-jwt-token')
// Custom authorization header
.withAuthorization('Custom scheme-and-token')
Requests with Body
For POST, PUT, and PATCH requests, you can set a body:
// JSON body (automatically sets Content-Type: application/json)
.withBody({ name: 'John', age: 30 })
// String body
.withBody('Plain text content')
// FormData
const form = new FormData();
form.append('file', fileBlob);
.withBody(form)
// Blob body
const blob = new Blob(['Hello, world!'], { type: 'text/plain' });
const request = create.post()
.withBody(blob)
.sendTo('https://api.example.com/upload');
// ArrayBuffer body
const buffer = new ArrayBuffer(8);
const request = create.post()
.withBody(buffer)
.sendTo('https://api.example.com/upload');
// ReadableStream body
const stream = new ReadableStream();
const request = create.post()
.withBody(stream)
.sendTo('https://api.example.com/upload');
Response Handling
The library provides convenient methods for handling responses that can be chained directly after sendTo()
:
// Parse as JSON with type inference
const jsonData = await create.get()
.sendTo('https://api.example.com/data')
.json<MyDataType>();
// Get as text
const text = await create.get()
.sendTo('https://api.example.com/text')
.text();
// Get as blob
const blob = await create.get()
.sendTo('https://api.example.com/file')
.blob();
// Get as ArrayBuffer
const buffer = await create.get()
.sendTo('https://api.example.com/binary')
.arrayBuffer();
// Get as ReadableStream
const stream = await create.get()
.sendTo('https://api.example.com/stream')
.body();
// For more control, you can access the raw response first
const response = await create.get().sendTo('https://api.example.com/data');
const status = response.status;
const headers = response.headers;
const data = await response.json();
Error Handling
The library provides a RequestError
class with details about failed requests:
try {
const data = await create.get()
.sendTo('https://api.example.com/data')
.json();
} catch (error) {
if (error instanceof RequestError) {
console.log(error.message); // Error message
console.log(error.status); // HTTP status code (if available)
console.log(error.url); // Request URL
console.log(error.method); // HTTP method
console.log(error.timeoutError); // Whether it was a timeout
console.log(error.response); // Raw response (if available)
}
}
Advanced Usage
Handling File Downloads
const blob = await create.get()
.withHeaders({ Accept: 'application/pdf' })
.sendTo('https://api.example.com/reports/123/download')
.blob();
const url = window.URL.createObjectURL(blob);
// Create a download link
const a = document.createElement('a');
a.href = url;
a.download = 'report.pdf';
a.click();
// Clean up
window.URL.revokeObjectURL(url);
Working with AbortController
const controller = new AbortController();
// Pass the controller to the request
try {
const data = await create.get()
.withAbortController(controller)
.sendTo('https://api.example.com/data')
.json();
} catch (error) {
// Handle the aborted request
}
// Cancel the request after 2 seconds
setTimeout(() => controller.abort(), 2000);
Request Priority
import { RequestPriority } from 'create-request';
// High priority for critical resources
const userProfile = await create.get()
.withPriority(RequestPriority.HIGH)
.sendTo('https://api.example.com/user/profile')
.json();
// Low priority for non-critical resources
const recommendations = await create.get()
.withPriority(RequestPriority.LOW)
.sendTo('https://api.example.com/recommendations')
.json();
Creating Reusable Request Configurations
import { RequestPriority, CredentialsPolicy } from 'create-request';
function createAuthenticatedRequest(token) {
return create.get()
.withBearerToken(token)
.withHeaders({
'X-App-Version': '1.0.0',
})
.withTimeout(10000)
.withRetries(2)
.withCredentials(CredentialsPolicy.INCLUDE)
.withPriority(RequestPriority.HIGH);
}
// Later use the configured request
const users = await createAuthenticatedRequest(myToken)
.sendTo('https://api.example.com/users')
.json();
Cookie Management
// Set multiple cookies in a single request
const response = await create.get()
.withCookies({
sessionId: 'abc123',
theme: 'dark',
region: 'us-east',
'analytics-opt-in': 'true'
})
.sendTo('https://api.example.com/dashboard');
// Combine with other header settings
const response = await create.get()
.withCookies({ sessionId: 'abc123' })
.withBearerToken('your-token-here')
.sendTo('https://api.example.com/protected-resource');
Environment-Specific Limitations
Browser Limitations
CORS Restrictions - Browsers enforce Same-Origin Policy and CORS. Requests to different origins must have proper CORS headers set up server-side.
Cookies and Authentication - Third-party cookies might be blocked depending on browser settings. Use
withCredentials(CredentialsPolicy.INCLUDE)
for cross-origin authenticated requests.Mixed Content - Browsers block HTTP requests from HTTPS pages. Always use HTTPS endpoints in production.
Content Security Policy (CSP) - If your application uses CSP, you must configure it to allow connections to your API endpoints.
Priority Hints - The
withPriority
feature is only supported in some modern browsers and may be ignored in others.KeepAlive - The
withKeepAlive
feature allows requests to outlive the page but has varying support across browsers.
Server-Side (Node.js) Limitations
Missing Node.js Fetch - Prior to Node 18, the Fetch API isn't available natively.
Self-signed Certificates - Node.js rejects self-signed certificates by default. You may need additional configuration for development environments.
Memory Management - When downloading large files or processing large responses, be mindful of memory consumption, especially when using
arrayBuffer()
orblob()
.Proxy Support - Additional configuration may be needed to work with HTTP proxies in Node.js.
Limited Feature Support - Features like
withKeepAlive
andwithPriority
might not be relevant or fully supported in Node.js environments.
Browser Compatibility
This library works with all browsers that support the Fetch API:
- Chrome 42+
- Firefox 39+
- Safari 10.1+
- Edge 14+
- Opera 29+
Comparison with Other Libraries
Feature | create-request | axios | fetch |
---|---|---|---|
Bundle Size | Small (~2KB) | Medium (~14KB) | Smallest (built-in) |
TypeScript Support | Full with type inference | Partial | Manual |
Type Safety | Enums for constants | Limited | None |
Chainable API | β Yes | β No | β No |
Request Cancellation | β Yes (AbortController) | β Yes | β Yes (manual) |
Auto JSON | β Yes | β Yes | β No |
Timeout | β Yes | β Yes | β No (manual) |
Retries | β Yes | β No (needs addon) | β No (manual) |
Query params with arrays | β Yes | β Yes | β No (manual) |
Request Priority | β Yes (simple API) | β No | β Yes (via options) |
KeepAlive | β Yes (simple API) | β No | β Yes (via options) |
Learning Curve | Low | Medium | Medium |
Why Not Just Use fetch?
like JSON parsing The native Fetch API is powerful but low-level, requiring you to:ing
- Write boilerplate for common tasks like JSON parsing
- Manually implement timeout handlingeps
- Create your own retry mechanisms- Parse non-2xx responses manually
- Handle errors across multiple steps
- Parse non-2xx responses manually
create-request
gives you all these features with a cleaner API that's designed specifically for modern TypeScript applications.
create-request
gives you all these features with a cleaner API that's designed specifically for modern TypeScript applications.### Why Not axios?
Why Not axios?est` offers:
While axios is popular, create-request
offers:er)
- Smaller bundle size (~85% smaller)n
- More complete TypeScript integration
- Built-in retries without addons- Modern features like priority hints
- Modern features like priority hintsainable API
- A more intuitive, chainable API
Real-world Examples
t for Authentication
API Client for Authentication
// Create a reusable authentication client
function createAuthClient(baseUrl) {
return {{
login: async (email, password) => {
return create.post()
.withBody({ email, password })eout(3000)
.withTimeout(3000) .sendTo(`${baseUrl}/auth/login`)
.sendTo(`${baseUrl}/auth/login`) .json();
.json();
},
en) => {
refreshToken: async (refreshToken) => {t()
return create.post()
.withBody({ refreshToken })ries(2)
.withRetries(2) .sendTo(`${baseUrl}/auth/refresh`)
.sendTo(`${baseUrl}/auth/refresh`) .json();
.json(); }
} };
};
}
// Use it in your applicationst authClient = createAuthClient('https://api.example.com');
const authClient = createAuthClient('https://api.example.com');const { token, user } = await authClient.login('user@example.com', 'password');
const { token, user } = await authClient.login('user@example.com', 'password');
ad with Progress
File Upload with Progress
// Upload a file with abort capabilityconst uploadFile = async (file, onProgress) => {
const uploadFile = async (file, onProgress) => {Controller();
const controller = new AbortController();
const form = new FormData();
const form = new FormData();append('file', file);
form.append('file', file);
try {
return await create.post()
.withBody(form)oller(controller)
.withAbortController(controller)or large files
.withTimeout(60000) // 1 minute timeout for large filesries(2)
.withRetries(2)s://api.example.com/upload')
.sendTo('https://api.example.com/upload')
.json();
} catch (error) {f (error instanceof RequestError) {
if (error instanceof RequestError) {ror(`Upload failed: ${error.message}`);
console.error(`Upload failed: ${error.message}`); }
} throw error;
throw error;
}
turn {
return { abort: () => controller.abort()
abort: () => controller.abort();
};};
};
License
MIT
4 months ago