244 lines
No EOL
5 KiB
Markdown
244 lines
No EOL
5 KiB
Markdown
# TypedFetch
|
|
|
|
> Type-safe HTTP client that doesn't suck - Fetch for humans who have stuff to build
|
|
|
|
Zero dependencies. Full type safety. Just works.
|
|
|
|
📦 **npm**: `@catalystlabs/typedfetch`
|
|
🌐 **Website**: [typedfetch.dev](https://typedfetch.dev)
|
|
📚 **Docs**: [typedfetch.dev/docs](https://typedfetch.dev/docs)
|
|
💻 **Source**: [git.catalystlab.cc/caseycollier/TypeFetched](https://git.catalystlab.cc/caseycollier/TypeFetched)
|
|
|
|
## 🚀 Quick Start
|
|
|
|
```bash
|
|
# Using bun
|
|
bun add @catalystlabs/typedfetch
|
|
|
|
# Using npm
|
|
npm install @catalystlabs/typedfetch
|
|
```
|
|
|
|
```typescript
|
|
import { tf } from '@catalystlabs/typedfetch'
|
|
|
|
// Zero config - just works!
|
|
const { data, response } = await tf.get('https://api.github.com/users/github')
|
|
console.log(data.name) // Full response data
|
|
|
|
// With configuration
|
|
import { createTypedFetch } from '@catalystlabs/typedfetch'
|
|
|
|
const client = createTypedFetch({
|
|
baseURL: 'https://api.example.com',
|
|
headers: {
|
|
'Authorization': 'Bearer token'
|
|
}
|
|
})
|
|
|
|
const { data, response } = await client.get('/users/123')
|
|
```
|
|
|
|
## ✨ Features
|
|
|
|
### 🔒 Type Safety
|
|
- TypeScript inference for response data
|
|
- No manual type casting needed
|
|
- Type-safe error handling
|
|
|
|
### 🛡️ Built-in Resilience
|
|
- Automatic retries with exponential backoff
|
|
- Circuit breaker for failing endpoints
|
|
- Request caching (memory + IndexedDB)
|
|
- HTTP cache header respect
|
|
|
|
### 🚀 Simple API
|
|
- Clean, chainable API
|
|
- Standard HTTP methods: get(), post(), put(), delete()
|
|
- Consistent response format
|
|
- Zero boilerplate
|
|
|
|
### ⚡ Performance
|
|
- <15KB gzipped bundle
|
|
- Zero runtime dependencies
|
|
- Efficient caching
|
|
- Request deduplication
|
|
|
|
## 📚 Documentation
|
|
|
|
### Basic Usage
|
|
|
|
```typescript
|
|
import { tf } from '@catalystlabs/typedfetch'
|
|
|
|
// GET request
|
|
const { data, response } = await tf.get('https://api.example.com/users')
|
|
|
|
// POST request
|
|
const { data, response } = await tf.post('https://api.example.com/users', {
|
|
body: {
|
|
name: 'John Doe',
|
|
email: 'john@example.com'
|
|
}
|
|
})
|
|
|
|
// PUT request
|
|
const { data, response } = await tf.put('https://api.example.com/users/123', {
|
|
body: {
|
|
name: 'Jane Doe'
|
|
}
|
|
})
|
|
|
|
// DELETE request
|
|
const { data, response } = await tf.delete('https://api.example.com/users/123')
|
|
```
|
|
|
|
### Configuration
|
|
|
|
```typescript
|
|
import { createTypedFetch } from '@catalystlabs/typedfetch'
|
|
|
|
const client = createTypedFetch({
|
|
baseURL: 'https://api.example.com',
|
|
headers: {
|
|
'Authorization': 'Bearer token',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
timeout: 30000,
|
|
|
|
// Retry configuration
|
|
retry: {
|
|
attempts: 3,
|
|
delay: 1000,
|
|
maxDelay: 10000,
|
|
backoff: 'exponential'
|
|
},
|
|
|
|
// Cache configuration
|
|
cache: {
|
|
enabled: true,
|
|
ttl: 300000, // 5 minutes
|
|
storage: 'memory' // or 'indexeddb'
|
|
}
|
|
})
|
|
|
|
// Or configure the global instance
|
|
import { tf } from '@catalystlabs/typedfetch'
|
|
|
|
tf.configure({
|
|
baseURL: 'https://api.example.com',
|
|
headers: {
|
|
'Authorization': 'Bearer token'
|
|
}
|
|
})
|
|
```
|
|
|
|
### Response Format
|
|
|
|
All methods return a consistent response format:
|
|
|
|
```typescript
|
|
const { data, response } = await tf.get('/endpoint')
|
|
|
|
// data: The parsed response body
|
|
// response: The full Response object from fetch API
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
```typescript
|
|
try {
|
|
const { data, response } = await tf.get('/users/123')
|
|
// Handle success
|
|
} catch (error) {
|
|
if (error.response) {
|
|
// Server responded with error status
|
|
console.log(error.response.status)
|
|
console.log(error.data)
|
|
} else if (error.request) {
|
|
// Request was made but no response received
|
|
console.log('Network error:', error.message)
|
|
} else {
|
|
// Something else happened
|
|
console.log('Error:', error.message)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Advanced Features
|
|
|
|
#### Circuit Breaker
|
|
Automatically stops making requests to failing endpoints:
|
|
|
|
```typescript
|
|
const client = createTypedFetch({
|
|
circuitBreaker: {
|
|
enabled: true,
|
|
failureThreshold: 5,
|
|
resetTimeout: 60000 // 1 minute
|
|
}
|
|
})
|
|
```
|
|
|
|
#### Request Caching
|
|
Intelligent caching with multiple storage options:
|
|
|
|
```typescript
|
|
const client = createTypedFetch({
|
|
cache: {
|
|
enabled: true,
|
|
ttl: 300000, // 5 minutes
|
|
storage: 'indexeddb', // Persistent storage
|
|
respectCacheHeaders: true // Honor HTTP cache headers
|
|
}
|
|
})
|
|
```
|
|
|
|
#### Custom Headers per Request
|
|
```typescript
|
|
const { data } = await tf.get('/endpoint', {
|
|
headers: {
|
|
'X-Custom-Header': 'value'
|
|
}
|
|
})
|
|
```
|
|
|
|
## 🎯 Why TypedFetch?
|
|
|
|
### vs Axios
|
|
- ✅ Built on modern fetch API
|
|
- ✅ Smaller bundle size
|
|
- ✅ Better TypeScript support
|
|
- ✅ Built-in resilience features
|
|
|
|
### vs Native Fetch
|
|
- ✅ Automatic JSON parsing
|
|
- ✅ Better error handling
|
|
- ✅ Built-in retries and caching
|
|
- ✅ Simpler API
|
|
|
|
## 📦 Bundle Size
|
|
|
|
- Core: <15KB gzipped
|
|
- Zero runtime dependencies
|
|
- Tree-shakeable
|
|
- Works without build step
|
|
|
|
## 🌐 Browser Support
|
|
|
|
- Modern browsers (ES2020+)
|
|
- Node.js 18+
|
|
- Deno
|
|
- Bun
|
|
|
|
## 🤝 Contributing
|
|
|
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
|
|
## 📄 License
|
|
|
|
MIT License - see [LICENSE](LICENSE) for details.
|
|
|
|
---
|
|
|
|
**TypedFetch**: Because life's too short for complex HTTP clients. 🚀 |