Documentation Index
Fetch the complete documentation index at: https://www.orionjs.com/llms.txt
Use this file to discover all available pages before exploring further.
The createTPaginatedQuery function creates a sub-router with three separate procedures for paginated tables: getItems, getCount, and getDescription. Each procedure has its own typed input and output.
Basic Usage
import {Procedures, TPaginatedQuery, createTPaginatedQuery} from '@orion-js/trpc'
import {Inject} from '@orion-js/services'
import {UsersRepo} from 'app/users/repos/UsersRepo'
@Procedures()
export class UserProcedures {
@Inject(() => UsersRepo)
private usersRepo: UsersRepo
@TPaginatedQuery()
listUsers = createTPaginatedQuery({
params: {
search: {type: String, optional: true},
},
allowedSorts: ['name', 'createdAt'],
defaultSortBy: 'createdAt',
defaultSortType: 'desc',
getItems: async ({skip, limit, sort}, {search}, viewer) => {
const query = search ? {name: {$regex: search, $options: 'i'}} : {}
return this.usersRepo.find(query).skip(skip).limit(limit).sort(sort).toArray()
},
getCount: async ({search}, viewer) => {
const query = search ? {name: {$regex: search, $options: 'i'}} : {}
return this.usersRepo.countDocuments(query)
},
})
}
The getItems function receives pagination parameters as its first argument, ready to use with MongoDB:
interface PaginationParams {
skip: number // Calculated from page and limit
limit: number // Items per page
sort: {[key: string]: 1 | -1} // Sort object for MongoDB
}
Example:
getItems: async ({skip, limit, sort}, params, viewer) => {
// Use directly with MongoDB
return collection.find(query).skip(skip).limit(limit).sort(sort).toArray()
}
Procedures
Each createTPaginatedQuery returns three separate tRPC procedures, accessible as nested routes:
getItems
Returns the paginated items. Input includes pagination fields and custom params.
const result = await caller.listUsers.getItems({
page: 1,
limit: 10,
sortBy: 'name',
sortType: 'asc',
params: {search: 'john'},
})
// Returns: { items: User[] }
getCount
Returns the total count (for calculating total pages on the client). Input includes only custom params.
const result = await caller.listUsers.getCount({
params: {search: 'john'},
})
// Returns: { totalCount: number }
getDescription
Returns the sorting configuration. Takes no input.
const result = await caller.listUsers.getDescription()
// Returns: { allowedSorts: string[], defaultSortBy?: string, defaultSortType?: 'asc' | 'desc' }
Options
| Option | Type | Description |
|---|
params | Schema | Custom input params schema (optional) |
returns | Schema | Output item schema for cleaning (optional) |
getItems | Function | (paginationParams, params, viewer) => items[] - Returns the paginated items |
getCount | Function | (params, viewer) => number - Returns the total count for the query |
allowedSorts | string[] | List of allowed sort fields (optional) |
defaultSortBy | string | Default sort field (optional) |
defaultSortType | ’asc’ | ‘desc’ | Default sort direction (optional) |
defaultLimit | number | Default page size (default: 20) |
maxLimit | number | Maximum allowed page size (default: 200) |
The following fields are available in the getItems input:
| Field | Type | Default | Description |
|---|
page | integer | 1 | Page number (min: 1) |
limit | integer | 20 | Items per page (min: 0, max: 200) |
sortBy | string | - | Sort field (only if allowedSorts is configured) |
sortType | ’asc’ | ‘desc’ | - | Sort direction |
Type Inference
Item types are automatically inferred from the getItems return type - no manual type annotations needed:
interface User {
id: string
name: string
email: string
}
@TPaginatedQuery()
listUsers = createTPaginatedQuery({
getItems: async ({skip, limit, sort}) => {
// Return type User[] is inferred automatically
const users: User[] = await this.usersRepo.find({}).skip(skip).limit(limit).sort(sort).toArray()
return users
},
getCount: async () => this.usersRepo.countDocuments({}),
})
For proper type inference with InferRouterOutputs, use the custom InferRouterOutputs type from @orion-js/trpc:
import {InferRouterOutputs} from '@orion-js/trpc'
type RouterOutputs = InferRouterOutputs<typeof router>
type ListUsersOutput = RouterOutputs['listUsers']
// ListUsersOutput.getItems.items is correctly typed as User[]
// ListUsersOutput.getCount.totalCount is number
// ListUsersOutput.getDescription.allowedSorts is string[]
Schema Cleaning
If you provide a returns schema, items will be cleaned/filtered according to that schema:
@TPaginatedQuery()
listUsers = createTPaginatedQuery({
returns: {id: {type: 'ID'}, name: {type: String}},
getItems: async ({skip, limit, sort}) => {
return this.usersRepo.find({}).skip(skip).limit(limit).sort(sort).toArray()
},
getCount: async () => this.usersRepo.countDocuments({}),
})
// Only 'id' and 'name' fields will be returned, extra fields are stripped
Client Usage Example
// In your paginated table component
const [page, setPage] = useState(1)
const [sortBy, setSortBy] = useState<string>()
const [sortType, setSortType] = useState<'asc' | 'desc'>()
// Fetch items
const {data: itemsResult} = trpc.listUsers.getItems.useQuery({
page,
limit: 10,
sortBy,
sortType,
params: {},
})
// Fetch count for pagination
const {data: countResult} = trpc.listUsers.getCount.useQuery({
params: {},
})
// Fetch description for sort options
const {data: descriptionResult} = trpc.listUsers.getDescription.useQuery()
// Access data directly - each procedure has its own specific return type
const items = itemsResult?.items ?? []
const totalCount = countResult?.totalCount ?? 0
const allowedSorts = descriptionResult?.allowedSorts ?? []
const totalPages = Math.ceil(totalCount / 10)