@daemoniorum/api
v0.1.1
Published
API client library with Axios, retry logic, and TanStack Query integration
Maintainers
Readme
@daemoniorum/api
API client library with Axios, retry logic, and TanStack Query integration for React applications.
Features
| Feature | Description |
|---------|-------------|
| Axios Client | Pre-configured with retry logic, timeouts, and interceptors |
| Token Refresh | Automatic token refresh with request queuing |
| Error Handling | Structured ApiError class with status codes |
| TanStack Query | Ready-to-use hooks for queries and mutations |
| TypeScript | Full type safety throughout |
Installation
npm install @daemoniorum/api axios @tanstack/react-query
# or
pnpm add @daemoniorum/api axios @tanstack/react-queryQuick Start
1. Create API Client
import { createApiClient } from '@daemoniorum/api/client'
const api = createApiClient({
baseUrl: 'https://api.example.com',
getToken: () => localStorage.getItem('token'),
refreshToken: async () => {
const response = await fetch('/auth/refresh', {
method: 'POST',
body: JSON.stringify({
refreshToken: localStorage.getItem('refresh_token'),
}),
})
return response.json()
},
onAuthFailure: () => {
window.location.href = '/login'
},
})2. Setup Query Client
import { QueryClientProvider } from '@tanstack/react-query'
import { createQueryClient } from '@daemoniorum/api/query'
const queryClient = createQueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
)
}3. Use Query Hooks
import { useApiQuery, useApiMutation } from '@daemoniorum/api/query'
// Fetch data
function useUsers() {
return useApiQuery(api, ['users'], '/users')
}
// Create mutation
function useCreateUser() {
return useApiMutation(api, 'POST', '/users', {
invalidateKeys: [['users']],
})
}
// Use in component
function UserList() {
const { data, isLoading, error } = useUsers()
const createUser = useCreateUser()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{data.map(user => <UserRow key={user.id} user={user} />)}
<button onClick={() => createUser.mutate({ name: 'New User' })}>
Add User
</button>
</div>
)
}API Reference
Client
createApiClient(config)
Create a configured Axios instance.
interface ApiClientConfig {
baseUrl: string
timeout?: number // Default: 20000ms
headers?: Record<string, string>
getToken?: () => string | null | Promise<string | null>
refreshToken?: () => Promise<TokenRefreshResult>
storageKeys?: {
accessToken?: string // Default: 'access_token'
refreshToken?: string // Default: 'refresh_token'
tokenType?: string // Default: 'token_type'
}
retry?: {
retries?: number // Default: 2
exponentialDelay?: boolean // Default: true
retryCondition?: (error: unknown) => boolean
onRetry?: (retryCount: number, error: unknown) => void
}
onAuthFailure?: () => void
}ApiError
Structured error class with helper methods.
try {
await api.get('/protected')
} catch (error) {
if (error instanceof ApiError) {
if (error.isAuthError()) {
// Handle 401
}
if (error.isValidationError()) {
// Handle 400/422
}
if (error.isRetryable) {
// Can retry this request
}
}
}Query Hooks
useApiQuery(client, queryKey, url, options?)
Fetch data with caching.
const { data, isLoading, error, refetch } = useApiQuery(
api,
['users', { active: true }],
'/users',
{
config: { params: { active: true } },
staleTime: 1000 * 60 * 5,
enabled: !!userId,
}
)useApiMutation(client, method, url, options?)
Create, update, or delete data.
const mutation = useApiMutation(api, 'POST', '/users', {
invalidateKeys: [['users']],
onSuccess: (data) => {
console.log('Created:', data)
},
})
mutation.mutate({ name: 'John', email: '[email protected]' })useApiInfiniteQuery(client, queryKey, url, options?)
Paginated/infinite scroll queries.
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useApiInfiniteQuery<PaginatedResponse<User>>(
api,
['users', 'infinite'],
'/users',
{ pageSize: 20 }
)
// Flatten pages
const allUsers = data?.pages.flatMap(page => page.content) ?? []Utilities
useInvalidateQueries()
Manually invalidate cached queries.
const invalidate = useInvalidateQueries()
// Refresh users list
invalidate(['users'])usePrefetchQuery(client)
Prefetch data on hover/focus.
const prefetch = usePrefetchQuery(api)
<Link
to={`/users/${id}`}
onMouseEnter={() => prefetch(['user', id], `/users/${id}`)}
>
View User
</Link>Modules
| Module | Import | Description |
|--------|--------|-------------|
| Client | @daemoniorum/api/client | Axios client factory |
| Query | @daemoniorum/api/query | TanStack Query integration |
| Types | @daemoniorum/api/types | TypeScript types |
Error Codes
| Code | Status | Description |
|------|--------|-------------|
| NETWORK_ERROR | - | Unable to reach server |
| OFFLINE | - | Browser is offline |
| TIMEOUT | - | Request timed out |
| UNAUTHORIZED | 401 | Authentication required |
| FORBIDDEN | 403 | Permission denied |
| NOT_FOUND | 404 | Resource not found |
| VALIDATION_ERROR | 400/422 | Invalid input |
| RATE_LIMITED | 429 | Too many requests |
| SERVER_ERROR | 5xx | Server error |
License
MIT
