Features: - Zero configuration, just works out of the box - Runtime type inference and validation - Built-in caching with W-TinyLFU algorithm - Automatic retries with exponential backoff - Circuit breaker for resilience - Request deduplication - Offline support with queue - OpenAPI schema discovery - Full TypeScript support with type descriptors - Modular architecture - Configurable for advanced use cases Built with bun, ready for npm publishing
263 lines
No EOL
6.1 KiB
Markdown
263 lines
No EOL
6.1 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
npm install typedfetch
|
|
```
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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](CONTRIBUTING.md) for details.
|
|
|
|
## 📄 License
|
|
|
|
MIT License - see [LICENSE](LICENSE) for details.
|
|
|
|
## 🔗 Links
|
|
|
|
- [Documentation](https://typedfetch.dev)
|
|
- [Examples](./examples)
|
|
- [GitHub](https://github.com/typedfetch/typedfetch)
|
|
- [NPM](https://www.npmjs.com/package/typedfetch)
|
|
|
|
---
|
|
|
|
**TypedFetch**: Because life's too short for `any` types and network errors. 🚀 |