paginationHandler
The paginationHandler middleware provides automatic pagination handling for API endpoints. It simplifies extracting pagination parameters, calculating offsets, generating metadata, and optionally integrates with a dynamic data source (e.g., database, ORM).
Import
import { paginationHandler } from "tezx/middleware";Options
export type PaginationOptions<
DataKey extends string = "data",
CountKey extends string = "total",
Item = any
> = {
defaultPage?: number; // Default page if not specified (default: 1)
defaultLimit?: number; // Default items per page (default: 10)
maxLimit?: number; // Maximum items per page (default: 100)
queryKeyPage?: string; // Query parameter for page (default: "page")
queryKeyLimit?: string; // Query parameter for limit (default: "limit")
countKey?: CountKey; // Key for total count (default: "total")
dataKey?: DataKey; // Key for data array (default: "data")
getDataSource?: <T extends Record<string, any> = {}>(
ctx: Context<T>,
pagination: { page: number; limit: number; offset: number }
) => Promise<
{ [K in DataKey]: Item[] } & { [K in CountKey]: number }
>;
};Pagination Metadata
When pagination is applied, the following structure is available in ctx.pagination and response body:
type PaginationBodyType = {
[x: string]: any;
pagination: {
page: number; // Current page number
limit: number; // Items per page
offset: number; // Calculated offset
totalItems: number; // Total number of items
totalPages: number; // Total number of pages
hasNextPage: boolean; // Whether next page exists
hasPrevPage: boolean; // Whether previous page exists
nextPage: number|null; // Next page number (or null)
prevPage: number|null; // Previous page number (or null)
};
};Usage Examples
1. Basic Setup (No Data Source)
Extracts pagination parameters and makes them available via ctx.pagination.
import { paginationHandler } from "tezx/middleware";
app.get("/users", paginationHandler(), async (ctx) => {
const { page, limit, offset } = ctx.pagination;
ctx.json({ page, limit, offset });
});
// GET /users?page=2&limit=5
// → { "page": 2, "limit": 5, "offset": 5 }2. With Dynamic Data Source (Database Integration)
Fetches data and total count from your database and returns a structured response.
app.get(
"/products",
paginationHandler({
getDataSource: async (ctx, { page, limit, offset }) => {
const { rows, count } = await Product.findAndCountAll({
offset,
limit,
});
return { data: rows, total: count };
},
})
);Response example:
{
"data": [ /* 10 products */ ],
"total": 53,
"pagination": {
"page": 2,
"limit": 10,
"offset": 10,
"totalItems": 53,
"totalPages": 6,
"hasNextPage": true,
"hasPrevPage": true,
"nextPage": 3,
"prevPage": 1
}
}3. Custom Query Keys
app.get(
"/articles",
paginationHandler({
queryKeyPage: "p",
queryKeyLimit: "size",
}),
async (ctx) => {
const { page, limit } = ctx.pagination;
ctx.json({ page, limit });
}
);
// GET /articles?p=3&size=20
// → { "page": 3, "limit": 20 }4. Custom Data and Count Keys
app.get(
"/comments",
paginationHandler({
dataKey: "items",
countKey: "totalCount",
getDataSource: async (ctx, { offset, limit }) => {
const { rows, count } = await Comment.findAndCountAll({ offset, limit });
return { items: rows, totalCount: count };
},
})
);5. Strict Maximum Limit
app.get(
"/logs",
paginationHandler({
defaultLimit: 20,
maxLimit: 50, // Clients cannot request more than 50
}),
async (ctx) => {
ctx.json({ limit: ctx.pagination.limit });
}
);
// GET /logs?limit=500
// → limit will be capped at 50Best Practices
- Always enforce
maxLimitto prevent abuse (e.g., requesting huge datasets). - Use consistent query keys (
queryKeyPage,queryKeyLimit). - Custom response keys (
dataKey,countKey) simplify frontend integration. - Place
paginationHandlerbefore your route handler soctx.paginationis available. - If using
getDataSource, the middleware sets the response automatically; otherwise, you can manually usectx.pagination.
logger
Middleware for logging incoming HTTP requests and responses. Logs the method, pathname, status code, and execution time for each request. Useful for debugging and monitoring your TezX server.
poweredBy
Middleware for adding an X-Powered-By header to HTTP responses. This is commonly used to indicate the server or framework powering the application.