Skip to main content

Filtering & Search

List endpoints support filtering, searching, sorting, and pagination out of the box.

Basic Usage

const { data } = useProducts({
where: { category: 'electronics' },
orderBy: { field: 'price', direction: 'asc' },
search: 'laptop',
page: 1,
limit: 20,
})

Pagination

All list endpoints return paginated results:

const { data } = useProducts({ page: 2, limit: 50 })

// Response shape:
{
items: [...], // Array of records
total: 150, // Total count matching filters
page: 2, // Current page
limit: 50, // Items per page
hasMore: true, // Whether more pages exist
}

Default: page: 1, limit: 20

Filtering with where

Simple Equality

// Shorthand - implicit 'eq' operator
where: { status: 'active' }
where: { categoryId: '123' }

// Explicit operator
where: { status: { eq: 'active' } }

Text Operators

OperatorDescriptionExample
eqEquals{ name: { eq: 'iPhone' } }
neNot equals{ status: { ne: 'deleted' } }
containsContains substring{ name: { contains: 'phone' } }
startsWithStarts with{ sku: { startsWith: 'ELEC-' } }
endsWithEnds with{ email: { endsWith: '@gmail.com' } }

Number/Date Operators

OperatorDescriptionExample
eqEquals{ price: { eq: 100 } }
neNot equals{ quantity: { ne: 0 } }
gtGreater than{ price: { gt: 50 } }
gteGreater or equal{ stock: { gte: 10 } }
ltLess than{ price: { lt: 100 } }
lteLess or equal{ rating: { lte: 3 } }

Boolean

where: { isActive: true }
where: { isPublished: false }

Multiple Conditions

All conditions are AND'd together:

where: {
category: 'electronics',
price: { lt: 500 },
isActive: true,
}
// WHERE category = 'electronics' AND price < 500 AND is_active = true

The search parameter searches across all text fields:

const { data } = useProducts({ search: 'wireless headphones' })

This generates:

WHERE name LIKE '%wireless headphones%'
OR description LIKE '%wireless headphones%'
OR sku LIKE '%wireless headphones%'

Sorting

const { data } = useProducts({
orderBy: { field: 'price', direction: 'asc' },
})

// Or descending
orderBy: { field: 'createdAt', direction: 'desc' }

Valid directions: 'asc' | 'desc'

Complete Example

function ProductList() {
const [filters, setFilters] = useState({
category: 'electronics',
page: 1,
})

const { data, isLoading } = useProducts({
where: {
category: filters.category,
price: { lt: 1000 },
isActive: true,
},
orderBy: { field: 'price', direction: 'asc' },
search: filters.search,
page: filters.page,
limit: 20,
})

if (isLoading) return <Spinner />

return (
<div>
<p>Showing {data.items.length} of {data.total} products</p>

{data.items.map(product => (
<ProductCard key={product.id} product={product} />
))}

{data.hasMore && (
<button onClick={() => setFilters(f => ({ ...f, page: f.page + 1 }))}>
Load More
</button>
)}
</div>
)
}

tRPC Direct Usage

// Server-side or in API routes
const products = await trpc.product.list.query({
where: {
category: { eq: 'electronics' },
price: { gte: 100, lte: 500 },
},
orderBy: { field: 'name', direction: 'asc' },
page: 1,
limit: 50,
})