A zero dependency, developer experience focused, replacement for axious.
https://typedfetch.dev
examples | ||
manual | ||
src | ||
tests | ||
.eslintrc.json | ||
.gitignore | ||
.npmignore | ||
bun.lockb | ||
LICENSE | ||
package.json | ||
README.md | ||
tsconfig.json |
TypedFetch
Fetch for humans who have shit to build
TypedFetch is a next-generation HTTP client that brings type safety, intelligent error handling, and developer-friendly features to API communication. It eliminates the boilerplate and pain points of traditional fetch/axios approaches while providing a tRPC-like experience that works with any REST API.
🚀 Quick Start
npm install typedfetch
import { createTypedFetch } from 'typedfetch'
// Define your API structure
const client = createTypedFetch<{
users: {
get: (id: string) => User
list: () => User[]
create: (data: CreateUser) => User
}
}>({
baseURL: 'https://api.example.com'
})
// Use with full type safety
const { data, error } = await client.users.get('123')
if (error) {
// TypeScript knows the error structure!
switch (error.type) {
case 'not_found': // Handle 404
case 'network': // Handle network error
}
}
// TypeScript knows data is User | undefined
✨ Features
🔒 Type Safety Without Code Generation
- Full TypeScript inference throughout request/response cycle
- No more
any
types or manual casting - Compile-time URL validation
- Response type discrimination
🛡️ Unified Error Handling
- Categorized, actionable errors (network, HTTP, parsing, timeout, abort)
- Discriminated unions for type-safe error handling
- Automatic retry logic with exponential backoff
- Circuit breaker pattern for failing endpoints
🚀 Zero Boilerplate
- Define once, use everywhere
- Intelligent defaults
- Proxy-based API with dot notation
- Auto-injected auth tokens
⚡ Built-in Resilience
- Request retry with smart conditions
- Request deduplication
- Multi-tier caching (memory + IndexedDB)
- HTTP cache header respect
- Offline support
🔧 Developer Experience
- Setup in <5 minutes
- IntelliSense for everything
- Clear, actionable error messages
- Zero compile step required
📚 Documentation
Basic Usage
interface User {
id: string
name: string
email: string
}
interface CreateUserData {
name: string
email: string
}
const client = createTypedFetch<{
users: {
get: (id: string) => User
list: () => User[]
create: (data: CreateUserData) => User
update: (params: { id: string } & Partial<User>) => User
delete: (id: string) => void
}
}>({
baseURL: 'https://api.example.com',
auth: () => getToken(), // Auto-injected
timeout: 30000,
// Retry configuration
retry: {
attempts: 3,
delay: (attempt) => Math.min(1000 * Math.pow(2, attempt - 1), 10000),
condition: (error) => error.retryable
},
// Cache configuration
cache: {
storage: 'both', // memory + IndexedDB
ttl: {
'users.list': 300000, // 5 minutes
'users.get': 3600000, // 1 hour
}
}
})
Advanced Configuration
const client = createTypedFetch<API>({
baseURL: process.env.API_URL,
// Global interceptors
interceptors: {
request: [(config) => {
config.headers.authorization = `Bearer ${getToken()}`
return config
}],
response: [(response) => {
logMetrics(response)
return response
}]
},
// Request deduplication
dedupe: {
window: 1000, // 1 second
key: (config) => `${config.method}:${config.url}`
}
})
Error Handling
const { data, error, loading } = await client.users.get('123')
if (error) {
switch (error.type) {
case 'http':
if (error.status === 404) {
console.log('User not found')
} else if (error.status >= 500) {
console.log('Server error - will retry automatically')
}
break
case 'network':
console.log('Network error:', error.message)
break
case 'timeout':
console.log('Request timed out')
break
case 'parse':
console.log('Invalid response format')
break
case 'validation':
console.log('Response validation failed')
break
case 'abort':
console.log('Request was cancelled')
break
}
}
Transforms & Interceptors
import {
createDateTransform,
createCamelCaseTransform,
createLoggingInterceptor
} from 'typedfetch'
// Add request/response transforms
client.addRequestTransform(createSnakeCaseTransform())
client.addResponseTransform('users', createDateTransform())
client.addResponseTransform('users', createCamelCaseTransform())
// Add interceptors
const logging = createLoggingInterceptor({
logRequests: true,
logResponses: true
})
client.addRequestInterceptor(logging.request)
client.addResponseInterceptor(logging.response)
🏗️ Architecture
TypedFetch is built with a layered architecture:
- Layer 0: Core Types & Interfaces
- Layer 1: Protocol Abstraction (fetch/XHR/Node.js)
- Layer 2: Request Pipeline (interceptors, transforms)
- Layer 3: Resilience Core (retry, circuit breaker, deduplication)
- Layer 4: Cache Management (HTTP headers, LRU, IndexedDB)
- Layer 5: Developer API (Proxy-based interface)
🎯 Why TypedFetch?
vs Axios
- ✅ Type safety without manual interfaces
- ✅ Built-in retry, caching, deduplication
- ✅ Modern async/await patterns
- ✅ Smaller bundle size (<15KB)
vs tRPC
- ✅ Works with any backend (no server changes needed)
- ✅ REST API compatibility
- ✅ Gradual adoption
- ✅ Framework agnostic
vs React Query/SWR
- ✅ Framework agnostic
- ✅ Built-in HTTP client
- ✅ More control over requests
- ✅ Type-safe error handling
📦 Bundle Size
- Core: <15KB gzipped
- Zero runtime dependencies
- Tree-shakeable modules
- Works without build step
🌐 Browser Support
- Modern browsers (ES2020+)
- Node.js 16+
- Service Worker support
- IndexedDB for persistence
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
MIT License - see LICENSE for details.
🔗 Links
TypedFetch: Because life's too short for any
types and network errors. 🚀