@cassa/api-client
A typed fetch wrapper with automatic token injection, structured error handling, and TanStack Query integration. Replace the base URL and token getter to point at any backend.
Import
import { createApiClient, ApiError } from '@cassa/api-client'
import { CassaQueryProvider, createQueryClient } from '@cassa/api-client'1. Create the client
Create a singleton in lib/api.ts and import it wherever you need data.
// lib/api.ts
import { createApiClient } from '@cassa/api-client'
export const api = createApiClient({
baseUrl: process.env.NEXT_PUBLIC_API_URL ?? 'https://api.example.com',
getToken: () => {
if (typeof window === 'undefined') return null
return localStorage.getItem('access_token')
},
onError: (error) => {
if (error.status === 401) window.location.href = '/login'
},
})| Prop | Type | Default | Description |
|---|---|---|---|
| baseUrl* | string | — | Base URL prepended to every request path. |
| getToken | () => string | null | — | Called before each request. Returns the Bearer token or null. |
| onError | (error: ApiError) => void | — | Global error handler — useful for 401 redirects. |
2. Set up the provider
Wrap your app with CassaQueryProvider once in the root layout to enable TanStack Query caching throughout.
// app/providers.tsx
'use client'
import { CassaQueryProvider, createQueryClient } from '@cassa/api-client'
const queryClient = createQueryClient()
export const Providers = ({ children }) => (
<CassaQueryProvider client={queryClient}>
{children}
</CassaQueryProvider>
)3. Fetch data in components
'use client'
import { useQuery, queryOptions } from '@tanstack/react-query'
import { api, ApiError } from '../lib/api'
type User = { id: number; name: string; email: string }
const usersQuery = queryOptions({
queryKey: ['users'],
queryFn: () => api.get<User[]>('/users'),
})
export const UserList = () => {
const { data, isPending, isError, error } = useQuery(usersQuery)
if (isPending) return <p>Loading…</p>
if (isError) {
const status = error instanceof ApiError ? error.status : null
return <p>Failed{status ? ` (${status})` : ''}</p>
}
return <ul>{data.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}Mutations
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { api } from '../lib/api'
export const useCreateUser = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: (body: { name: string; email: string }) =>
api.post('/users', body),
onSuccess: () => qc.invalidateQueries({ queryKey: ['users'] }),
})
}API methods
| Prop | Type | Default | Description |
|---|---|---|---|
| api.get<T>(path, options?) | Promise<T> | — | GET request. |
| api.post<T>(path, body?, options?) | Promise<T> | — | POST request with JSON body. |
| api.put<T>(path, body?, options?) | Promise<T> | — | PUT request. |
| api.patch<T>(path, body?, options?) | Promise<T> | — | PATCH request. |
| api.delete<T>(path, options?) | Promise<T> | — | DELETE request. |