@bernierllc/validators-pagination-contract
v1.2.0
Published
Primitive schema validator for pagination contract validation (limit/offset, cursor-based)
Downloads
103
Readme
@bernierllc/validators-pagination-contract
Primitive schema validator for pagination contract validation (limit/offset, cursor-based).
Installation
npm install @bernierllc/validators-pagination-contractUsage
Basic Validation
import {
validatePaginationParams,
validatePaginationResponse,
isValidPagination,
} from '@bernierllc/validators-pagination-contract';
// Validate offset/limit pagination parameters
const problems = await validatePaginationParams({
limit: 20,
offset: 0,
});
if (problems.length === 0) {
console.log('Valid pagination parameters');
}
// Validate pagination response structure
const response = {
data: [{ id: 1 }, { id: 2 }],
meta: {
total: 100,
count: 2,
hasNext: true,
},
};
const responseProblems = await validatePaginationResponse(response);
// Quick validation check
const isValid = await isValidPagination({ limit: 10, offset: 0 });Cursor-based Pagination
import { validatePaginationParams } from '@bernierllc/validators-pagination-contract';
// Validate cursor pagination
const problems = await validatePaginationParams(
{
cursor: 'abc123',
limit: 20,
},
{
paginationType: 'cursor',
}
);
// Validate with after/before cursors
const cursorProblems = await validatePaginationParams(
{
after: 'xyz789',
limit: 10,
},
{
paginationType: 'cursor',
}
);Custom Validation Options
import { validatePaginationParams } from '@bernierllc/validators-pagination-contract';
const problems = await validatePaginationParams(
{
limit: 50,
offset: 100,
},
{
maxLimit: 100,
minLimit: 1,
allowNegativeOffset: false,
checkTotalCount: true,
checkNavigationLinks: true,
checkPageBoundaries: true,
}
);Type Detection
import { detectPaginationType } from '@bernierllc/validators-pagination-contract';
const type = detectPaginationType({ limit: 10, offset: 0 });
// Returns: 'offset'
const cursorType = detectPaginationType({ cursor: 'abc123' });
// Returns: 'cursor'
const pageType = detectPaginationType({ page: 1, limit: 10 });
// Returns: 'page'Parameter Normalization
import { normalizePaginationParams } from '@bernierllc/validators-pagination-contract';
// Normalize string parameters to numbers
const normalized = await normalizePaginationParams({
limit: '20',
offset: '0',
});
// Returns: { limit: 20, offset: 0 }
// Clamp values to allowed ranges
const clamped = await normalizePaginationParams(
{ limit: 200 },
{ maxLimit: 100 }
);
// Returns: { limit: 100 }API Reference
Main Functions
validatePaginationParams(data, options?, utils?)
Validates pagination parameters against contract conventions.
Parameters:
data(unknown) - Pagination parameters to validateoptions(PaginationContractOptions) - Validation options (optional)utils(SharedUtils) - Shared utilities (optional)
Returns: Promise<Problem[]> - Array of validation problems
validatePaginationResponse(response, options?, utils?)
Validates pagination response structure.
Parameters:
response(unknown) - Pagination response to validateoptions(PaginationContractOptions) - Validation options (optional)utils(SharedUtils) - Shared utilities (optional)
Returns: Promise<Problem[]> - Array of validation problems
isValidPagination(data, options?)
Quick check if pagination parameters are valid.
Parameters:
data(unknown) - Pagination parameters to checkoptions(PaginationContractOptions) - Validation options (optional)
Returns: Promise<boolean> - true if valid, false otherwise
detectPaginationType(data)
Detects the pagination type from parameters.
Parameters:
data(unknown) - Pagination parameters
Returns: PaginationType | null - Detected type ('offset', 'cursor', 'page') or null
normalizePaginationParams(data, options?)
Normalizes and validates pagination parameters.
Parameters:
data(unknown) - Raw pagination parametersoptions(PaginationContractOptions) - Validation options (optional)
Returns: Promise<Record<string, unknown> | null> - Normalized parameters or null if invalid
Validation Rules
The package includes the following validation rules:
offsetLimitRule
Validates offset/limit pagination parameters:
- Limit must be a positive integer within bounds
- Offset must be a non-negative integer (unless
allowNegativeOffsetis true) - Checks min/max limit constraints
cursorPaginationRule
Validates cursor-based pagination:
- Cursor must be a non-empty string
- Cannot specify both
afterandbeforecursors - Validates cursor format if enabled
responseStructureRule
Validates pagination response structure:
- Data field must be an array (if required)
- Metadata fields must have correct types
- Links must be valid URLs
totalCountRule
Validates total count consistency:
- Count must match data array length
- Count cannot exceed total
- Total pages calculation must be correct
- Current page must be within bounds
navigationLinksRule
Validates navigation links consistency:
hasNextmust match presence ofnextCursorornextlinkhasPreviousmust match presence ofpreviousCursororpreviouslink- Page numbers must be sequential
- Link URLs must be valid
Types
PaginationContractOptions
interface PaginationContractOptions {
paginationType?: 'offset' | 'cursor' | 'page';
checkTotalCount?: boolean;
checkNavigationLinks?: boolean;
checkPageBoundaries?: boolean;
maxLimit?: number;
minLimit?: number;
allowNegativeOffset?: boolean;
requireDataArray?: boolean;
validateCursorFormat?: boolean;
}PaginationResponse
interface PaginationResponse<T = unknown> {
data: T[];
meta?: PaginationMeta;
links?: {
next?: string;
previous?: string;
first?: string;
last?: string;
};
pagination?: PaginationMeta;
}Examples
Express API Middleware
import { validatePaginationParams, normalizePaginationParams } from '@bernierllc/validators-pagination-contract';
async function paginationMiddleware(req, res, next) {
const normalized = await normalizePaginationParams(req.query, {
maxLimit: 100,
minLimit: 1,
});
if (!normalized) {
return res.status(400).json({ error: 'Invalid pagination parameters' });
}
req.pagination = normalized;
next();
}Response Validation
import { validatePaginationResponse } from '@bernierllc/validators-pagination-contract';
async function validateApiResponse(response) {
const problems = await validatePaginationResponse(response, {
requireDataArray: true,
checkTotalCount: true,
checkNavigationLinks: true,
});
if (problems.length > 0) {
console.error('Response validation errors:', problems);
return false;
}
return true;
}GraphQL Pagination Validator
import { validatePaginationParams } from '@bernierllc/validators-pagination-contract';
async function validateGraphQLPagination(args) {
const problems = await validatePaginationParams(
{
after: args.after,
before: args.before,
limit: args.first || args.last,
},
{
paginationType: 'cursor',
maxLimit: 100,
}
);
if (problems.length > 0) {
throw new Error(`Invalid pagination: ${problems[0].message}`);
}
}Validation Severity
The validator reports problems with different severity levels:
- error: Critical issues that violate API contract (invalid types, out of bounds values)
- warning: Inconsistencies that may indicate bugs (mismatched flags and links)
Performance
- Lightweight validation with minimal overhead
- Synchronous type checks with async API for consistency
- No external dependencies beyond @bernierllc/validators-core
Integration Status
- Logger: not-applicable - Pure validation utility with no logging requirements
- Docs-Suite: ready - Complete TypeDoc documentation with markdown README
- NeverHub: not-applicable - Stateless validation utility with no service dependencies
See Also
- @bernierllc/validators-core - Core validation framework
- @bernierllc/validators-json-structure - JSON structure validation
- @bernierllc/validators-runner - Validation execution engine
License
Copyright (c) 2025 Bernier LLC. All rights reserved.
