QueryManager - Complete Guide
Basic Configuration
To create a QueryManager, you need to define:
- Data type (TypeScript interface)
 - CRUD methods for API communication
 - Optional configurations
 
import { QueryManager, PaginationResponse } from '@codeleap/query'
// 1. Define your data type
interface User {
  id: number
  name: string
  email: string
  created_at: string
}
// 2. Create the manager
const usersManager = new QueryManager<User>({
  name: 'users', // Unique name for identification
  itemType: {} as User, // Type reference
  queryClient: queryClient.client, // TanStack Query client
  // Required methods for CRUD operations
  listItems: async (limit, offset) => {
    const response = await api.get<PaginationResponse<User>>('/users/', {
      limit,
      offset,
    })
    return response.data
  },
  createItem: async (data) => {
    const response = await api.post<User>('/users/', data)
    return response.data
  },
  updateItem: async (data) => {
    const response = await api.patch<User>(`/users/${data.id}/`, data)
    return response.data
  },
  deleteItem: async (item) => {
    await api.delete(`/users/${item.id}/`)
    return item
  },
  retrieveItem: async (id) => {
    const response = await api.get<User>(`/users/${id}/`)
    return response.data
  },
})
Using QueryManager in Components
Complete Hook - use()
The use() method returns everything you need to manage a list of items:
function UsersScreen() {
  const {
    items,           // Array of users
    create,          // Function to create user
    update,          // Function to update user  
    delete: deleteUser, // Function to delete user
    refresh,         // Function to refresh list
    isRefreshing,    // Loading state
    getNextPage,     // Load next page
    getPreviousPage, // Load previous page
    itemMap,         // Map of items by ID
    queries,         // Mutation states
  } = usersManager.use()
  const handleCreateUser = async () => {
    const newUser = await create({
      name: 'John Silva',
      email: 'john@email.com',
    })
    console.log('User created:', newUser)
  }
  const handleUpdateUser = async (userId: number) => {
    await update({
      id: userId,
      name: 'Updated Name',
    })
  }
  const handleDeleteUser = async (user: User) => {
    await deleteUser(user)
  }
  return (
    <div>
      <button onClick={handleCreateUser}>Create User</button>
      <button onClick={refresh}>Refresh List</button>
      
      {items.map(user => (
        <div key={user.id}>
          <h3>{user.name}</h3>
          <p>{user.email}</p>
          <button onClick={() => handleUpdateUser(user.id)}>
            Edit
          </button>
          <button onClick={() => handleDeleteUser(user)}>
            Delete
          </button>
        </div>
      ))}
      
      <button onClick={() => getNextPage()}>
        Load More
      </button>
    </div>
  )
}
Individual Hooks
You can also use specific hooks for each operation:
Listing - useList()
function UsersList() {
  const {
    items,        // Array of users
    query,        // TanStack Query object
    refresh,      // Function to refresh
    isRefreshing, // Loading state
    itemCount,    // Total number of items
    getNextPage,  // Next page
  } = usersManager.useList({
    limit: 20,           // Items per page
    filter: {            // Optional filters
      status: 'active',
      role: 'admin',
    },
  })
  return (
    <div>
      <p>Total: {itemCount} users</p>
      {items.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  )
}
Creation - useCreate()
function CreateUserForm() {
  const { create, query } = usersManager.useCreate({
    optimistic: true,    // Optimistic updates
    appendTo: 'start',   // Add to beginning of list
  })
  const handleSubmit = async (formData) => {
    try {
      const newUser = await create(formData)
      console.log('User created:', newUser)
    } catch (error) {
      console.error('Creation error:', error)
    }
  }
  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
      <button 
        type="submit" 
        disabled={query.isPending}
      >
        {query.isPending ? 'Creating...' : 'Create User'}
      </button>
    </form>
  )
}
Update - useUpdate()
function EditUserForm({ userId }: { userId: number }) {
  const { update, query } = usersManager.useUpdate({
    optimistic: true, // Show changes immediately
  })
  const handleUpdate = async (data) => {
    await update({
      id: userId,
      ...data,
    })
  }
  return (
    <form onSubmit={handleUpdate}>
      {/* Form fields */}
      <button disabled={query.isPending}>
        Save
      </button>
    </form>
  )
}
Deletion - useDelete()
function DeleteUserButton({ user }: { user: User }) {
  const { delete: deleteUser, query } = usersManager.useDelete({
    optimistic: true, // Remove from list immediately
  })
  const handleDelete = () => {
    if (confirm('Confirm deletion?')) {
      deleteUser(user)
    }
  }
  return (
    <button 
      onClick={handleDelete}
      disabled={query.isPending}
    >
      {query.isPending ? 'Deleting...' : 'Delete'}
    </button>
  )
}
Retrieve Individual Item - useRetrieve()
function UserProfile({ userId }: { userId: number }) {
  const { data: user, query, refresh } = usersManager.useRetrieve({
    id: userId,
  })
  if (query.isLoading) return <div>Loading...</div>
  if (query.isError) return <div>Error loading</div>
  return (
    <div>
      <h1>{user?.name}</h1>
      <p>{user?.email}</p>
      <button onClick={refresh}>Refresh</button>
    </div>
  )
}