Skip to main content

QueryManager - Complete Guide

Basic Configuration

To create a QueryManager, you need to define:

  1. Data type (TypeScript interface)
  2. CRUD methods for API communication
  3. 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>
)
}