Skip to main content

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:

  1. Complete Documentation - Dive deep into all features and APIs
  2. Optimistic Updates - Learn how to implement instant UI feedback
  3. Cache Management - Master advanced caching strategies
  4. Error Handling - Implement robust error handling patterns
  5. 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