Query Helpers
Reusable Effect query helper schemas and types for paginated, filtered, and sorted API request and response envelopes.
bunx --bun shadcn@latest add https://krakstack.net/r/query-helpers.jsonOverview
query-helpers provides reusable Effect query helpers for API endpoints that accept paginated, filtered, and sorted query input and return paginated lists. Use Query for request validation, encodeSortParam and decodeSortParam for table sort query strings, PaginationMeta for response metadata, and PaginatedResponse to wrap item schemas in a consistent { data, meta } envelope.
Query Input
Query validates the request query parameters accepted by list endpoints:
page- zero-based page index. The first page is0.pageSize- number of records to request per page. Must be between1and100.globalFilter- optional free-text filter value for server-side search.sort- optional sort value encoded asfield:direction, where direction isascordesc.
Example query input:
{
page: 0,
pageSize: 10,
globalFilter: "housing",
sort: "publicName:asc",
}Sort Helpers
encodeSortParam converts the first TanStack Table-style sorting entry into a compact query string. decodeSortParam parses a sort query string back into { id, direction } and returns null when the input is missing or invalid.
const sort = encodeSortParam([{ id: "publicName", desc: false }]);
// "publicName:asc"
const parsed = decodeSortParam(sort);
// { id: "publicName", direction: "asc" }Included Files
lib/query.ts-Query,QueryType,SortDirection,SortState,SortParam,encodeSortParam,decodeSortParam,PaginationMeta,PaginatedResponse, and exported schema types
Usage
import { Query, PaginatedResponse, decodeSortParam } from "@/lib/query";
import { Record } from "@/services/record/schema";
export const PaginatedRecords = PaginatedResponse(Record);
HttpApiEndpoint.get("listRecords", "/records", {
query: Query,
success: PaginatedRecords,
});
const sort = decodeSortParam(query.sort);Return data with the matching envelope. meta.page and meta.pageSize should mirror the accepted query input, while total and pageCount describe the full result set:
return {
data: rows,
meta: {
page,
pageSize,
total,
pageCount: Math.ceil(total / pageSize),
},
};