Getting Started
Introduction
A powerful TypeScript library that provides type-safe CRUD operations with optimistic updates, infinite scroll pagination, and intelligent cache management built on top of React Query. Designed for modern React applications that need robust data management with excellent developer experience.
Important: This library is built on TanStack Query v5. You should have a solid understanding of React Query concepts like queries, mutations, cache management, and query keys before using this library. If you're new to React Query, we recommend reading the TanStack Query documentation first.
This library provides two main approaches for managing data operations:
- QueryManager: Full-featured CRUD operations with infinite scroll, optimistic updates, and automatic cache synchronization
- QueryOperations: Lightweight, type-safe query and mutation builder with fluent API
Key Features
- Type Safety: Full TypeScript support with automatic type inference for all operations
- Optimistic Updates: Instant UI updates with automatic rollback on errors
- Infinite Scroll: Built-in pagination support with seamless data loading
- Smart Caching: Intelligent cache synchronization between list and individual queries
- Dual Architecture: Choose between full-featured QueryManager or lightweight QueryOperations
- Zero Configuration: Works out of the box with sensible defaults
When to Use This Library
This library is ideal for applications that need:
- Complex data relationships with lists and individual items
- Real-time feel with optimistic updates
- Large datasets requiring pagination
- Strong type safety throughout the data layer
- Consistent caching across multiple components
TanStack Query Knowledge Required
You should be familiar with these TanStack Query concepts:
- Queries: How to fetch and cache data with
useQuery
- Mutations: How to modify data with
useMutation
- Query Keys: Understanding how React Query identifies and caches queries
- Cache Management: Invalidation, refetching, and optimistic updates
- Error Handling: How React Query handles and retries failed requests
New to TanStack Query? Read the official documentation and complete their tutorial before proceeding.
Installation
Install the library using your preferred package manager:
# Using npm
npm install @codeleap/query @tanstack/react-query
# Using yarn
yarn add @codeleap/query @tanstack/react-query
# Using pnpm
pnpm add @codeleap/query @tanstack/react-query
# Using bun
bun add @codeleap/query @tanstack/react-query
How This Library Extends TanStack Query
This library doesn't replace TanStack Query - it builds upon it by providing:
What TanStack Query Provides
- Core query and mutation functionality
- Caching, background updates, and synchronization
- Devtools and debugging capabilities
- Error handling and retry mechanisms
What This Library Adds
- Structured CRUD patterns with consistent APIs
- Automatic cache synchronization between list and detail views
- Optimistic updates with built-in rollback mechanisms
- Infinite scroll pagination with zero configuration
- Type-safe operation builders with automatic inference
- Advanced cache mutations for complex UI updates
Working Together
// You can still use TanStack Query directly alongside this library
import { useQuery } from '@tanstack/react-query'
import { createQueryManager } from '@codeleap/query'
// Direct TanStack Query usage
const customQuery = useQuery({
queryKey: ['custom-data'],
queryFn: fetchCustomData
})
// This library's enhanced patterns
const userManager = createQueryManager({
name: 'users',
// ... configuration
})
Quick Setup
1. Configure React Query
First, set up React Query in your application:
// main.tsx or App.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
retry: 3,
refetchOnWindowFocus: false,
},
mutations: {
retry: 1,
},
},
})
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
2. Define Your Data Types
Create TypeScript interfaces that extend QueryItem
:
// types/user.ts
import { QueryItem } from '@codeleap/query'
export interface User extends QueryItem {
id: string
name: string
email: string
status: 'active' | 'inactive'
role: 'admin' | 'user'
createdAt: string
updatedAt: string
}
export interface UserFilters {
status?: 'active' | 'inactive'
role?: 'admin' | 'user'
search?: string
}
Create your QueryManager
Perfect for full CRUD applications with lists and individual items:
import { createQueryManager } from '@codeleap/query'
import { userApi } from '../api/users'
import { User, UserFilters } from '../types/user'
export const usersManager = createQueryManager<Thread>({
name: 'users',
queryClient: queryClient.client,
async listFn(limit, offset, filters) {
const response = await fetch(`/api/users?${new URLSearchParams([limit, offset, filters])}`)
if (!response.ok) throw new Error('Failed to fetch users')
return response.json()
},
async retrieveFn(id) {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) throw new Error('Failed to fetch user')
return response.json()
},
async createFn(data) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!response.ok) throw new Error('Failed to create user')
return response.json()
},
async updateFn(data) {
const response = await fetch(`/api/users/${data?.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (!response.ok) throw new Error('Failed to update user')
return response.json()
},
async deleteFn(id) {
const response = await fetch(`/api/users/${id}`, { method: 'DELETE' })
if (!response.ok) throw new Error('Failed to delete user')
return id
},
})
Create your QueryOperations (For custom query patterns)
Ideal for custom query patterns with full type safety:
import { createQueryOperations } from '@codeleap/query'
import { userApi } from '../api/users'
export const userOperations = createQueryOperations({ queryClient })
.mutation('readUser', async (to) => {
await fetch(`/api/user/`, { method: 'POST', body: JSON.stringify({ read: to }) })
})
.query('listProperties', async () => {
const response = await fetch(`/api/user/properties`)
return response.json()
})
Your First Component
Now you can use the library in your components:
// components/UserList.tsx
import React from 'react'
import { userQueryManager } from '../hooks/useUserManager'
export function UserList() {
// Get paginated list with infinite scroll
const { items: users, query } = userQueryManager.useList({
filters: { status: 'active' },
limit: 20,
})
// Create mutation with optimistic updates
const createMutation = userQueryManager.useCreate({
optimistic: true,
appendTo: 'start',
listFilters: { status: 'active' },
})
const handleCreateUser = () => {
createMutation.mutate({
name: 'New User',
email: 'user@example.com',
status: 'active',
role: 'user',
})
}
if (query.isLoading) return <div>Loading users...</div>
if (query.error) return <div>Error: {query.error.message}</div>
return (
<div>
<button onClick={handleCreateUser} disabled={createMutation.isPending}>
{createMutation.isPending ? 'Creating...' : 'Add User'}
</button>
<div className="user-list">
{users.map((user) => (
<UserCard key={user.id} user={user} />
))}
</div>
{query.hasNextPage && (
<button
onClick={() => query.fetchNextPage()}
disabled={query.isFetchingNextPage}
>
{query.isFetchingNextPage ? 'Loading...' : 'Load More'}
</button>
)}
</div>
)
}
What's Next?
Now that you have the basics set up, you can explore more advanced features:
- Complete Documentation - Dive deep into all features and APIs
- Optimistic Updates - Learn how to implement instant UI feedback
- Cache Management - Master advanced caching strategies
- Error Handling - Implement robust error handling patterns
- Performance Optimization - Tips for handling large datasets efficiently
Quick Examples
Infinite Scroll List
const { items, query } = userQueryManager.useList({ limit: 50 })
// Automatically handles pagination, loading states, and errors
Optimistic Create
const createMutation = userQueryManager.useCreate({
optimistic: true,
appendTo: 'start'
})
// UI updates immediately, rolls back on error
Smart Caching
const { item } = userQueryManager.useRetrieve(userId)
// Uses list cache as initial data, syncs both ways
Type-Safe Operations
const userQuery = userOperations.useQuery('getUser', userId)
// Full TypeScript inference for parameters and return types
Need Help?
- Check the complete documentation for detailed examples
- Look at the TypeScript types for available options
- Use React Query DevTools to inspect cache state
- Consider the library's internal architecture for advanced use cases