typescriptintermediate
Cursor-Based Pagination API
Implement cursor-based pagination with forward/backward navigation, consistent ordering, and link headers.
typescriptPress ⌘/Ctrl + Shift + C to copy
interface PaginationParams {
cursor?: string;
limit?: number;
direction?: 'forward' | 'backward';
}
interface PaginatedResponse<T> {
data: T[];
pagination: {
hasNext: boolean;
hasPrev: boolean;
nextCursor: string | null;
prevCursor: string | null;
total?: number;
};
}
function encodeCursor(id: string, sortValue: string | number): string {
return Buffer.from(JSON.stringify({ id, sv: sortValue })).toString('base64url');
}
function decodeCursor(cursor: string): { id: string; sv: string | number } {
return JSON.parse(Buffer.from(cursor, 'base64url').toString());
}
// Simulated data
const items = Array.from({ length: 100 }, (_, i) => ({
id: `item-${String(i).padStart(3, '0')}`,
name: `Item ${i}`,
createdAt: new Date(2024, 0, 1 + i).toISOString(),
score: Math.floor(Math.random() * 1000),
}));
function paginate<T extends { id: string; createdAt: string }>(
allItems: T[],
params: PaginationParams,
): PaginatedResponse<T> {
const limit = Math.min(params.limit ?? 20, 100);
const sorted = [...allItems].sort((a, b) =>
b.createdAt.localeCompare(a.createdAt),
);
let startIdx = 0;
if (params.cursor) {
const { id } = decodeCursor(params.cursor);
const idx = sorted.findIndex(item => item.id === id);
if (idx >= 0) {
startIdx = params.direction === 'backward'
? Math.max(0, idx - limit)
: idx + 1;
}
}
const page = sorted.slice(startIdx, startIdx + limit);
const hasNext = startIdx + limit < sorted.length;
const hasPrev = startIdx > 0;
return {
data: page,
pagination: {
hasNext,
hasPrev,
nextCursor: hasNext && page.length > 0
? encodeCursor(page[page.length - 1].id, page[page.length - 1].createdAt)
: null,
prevCursor: hasPrev && page.length > 0
? encodeCursor(page[0].id, page[0].createdAt)
: null,
total: sorted.length,
},
};
}
// Test
const page1 = paginate(items, { limit: 5 });
console.log('Page 1:', page1.data.map(i => i.id));
console.log('Cursor:', page1.pagination.nextCursor);
if (page1.pagination.nextCursor) {
const page2 = paginate(items, { limit: 5, cursor: page1.pagination.nextCursor });
console.log('Page 2:', page2.data.map(i => i.id));
}Use Cases
- REST API pagination
- Database result pagination
- Infinite scroll backends
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptintermediate
Request Validation Schema Builder
Build a lightweight request validation layer with type inference for API endpoints.
Best for: API request body validation
#nodejs#validation
typescriptintermediate
Node.js Token Bucket Rate Limiter
Implement an in-memory token bucket rate limiter for controlling API request throughput.
Best for: Protecting APIs from abuse and DDoS
#nodejs#rate-limiting
typescriptbeginner
Native HTTP Server
Create a lightweight HTTP server using Node.js built-in http module with routing and JSON responses.
Best for: Lightweight API server without frameworks
#nodejs#http
typescriptbeginner
Fastify Server Setup
Create a high-performance Fastify server with schema validation, plugins, and typed routes.
Best for: High-performance REST APIs
#nodejs#fastify