### Quick Start: Kysely Cursor Pagination Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md A basic example demonstrating how to set up and use `createPaginator` with Kysely. It includes defining a database schema, creating a paginator with a custom codec pipeline (SuperJSON + Base64 URL), defining sort criteria, and paginating through results. ```typescript import { Kysely } from 'kysely' import { createPaginator, PostgresPaginationDialect, codecPipe, superJsonCodec, base64UrlCodec } from 'kysely-cursor' type DB = { users: { id: string; created_at: Date; email: string } } const db = new Kysely({ /* ... */ }) // Build a cursor codec: SuperJSON → Base64 URL (opaque & URL‑safe) const cursorCodec = codecPipe(superJsonCodec, base64UrlCodec) const paginator = createPaginator({ dialect: PostgresPaginationDialect, cursorCodec, }) const sorts = [ // nullable leading sorts are allowed; final sort must be unique & non‑nullable { col: 'users.created_at', dir: 'desc', output: 'created_at' }, { col: 'users.id', dir: 'desc', output: 'id' }, ] as const const page1 = await paginator.paginate({ query: db.selectFrom('users').select(['id', 'email', 'created_at']), sorts, limit: 25, }) const page2 = await paginator.paginate({ query: db.selectFrom('users').select(['id', 'email', 'created_at']), sorts, limit: 25, cursor: { nextPage: page1.nextPage! }, }) ``` -------------------------------- ### Install Kysely Cursor Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Instructions for installing the kysely-cursor package using pnpm, npm, or yarn. It also lists peer dependencies including Node.js version and Kysely version. ```bash # pnpm pnpm add kysely-cursor # npm npm i kysely-cursor # yarn yarn add kysely-cursor ``` -------------------------------- ### Database Dialects for Kysely Pagination Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Shows how to configure different database dialects for consistent pagination across various SQL systems like PostgreSQL, MySQL, MS SQL Server, and SQLite. Each dialect is instantiated using `createPaginator` with its corresponding dialect configuration. The example demonstrates a typical pagination query using the PostgreSQL dialect, highlighting the consistent API across all supported dialects. ```typescript import { PostgresPaginationDialect, MysqlPaginationDialect, MssqlPaginationDialect, SqlitePaginationDialect, createPaginator } from 'kysely-cursor' // PostgreSQL const pgPaginator = createPaginator({ dialect: PostgresPaginationDialect }) // MySQL const mysqlPaginator = createPaginator({ dialect: MysqlPaginationDialect }) // Microsoft SQL Server const mssqlPaginator = createPaginator({ dialect: MssqlPaginationDialect }) // SQLite const sqlitePaginator = createPaginator({ dialect: SqlitePaginationDialect }) // All dialects provide consistent API const result = await pgPaginator.paginate({ query: db.selectFrom('logs').select(['id', 'message', 'timestamp']), sorts: [ { col: 'timestamp', dir: 'desc' }, { col: 'id', dir: 'asc' } ] as const, limit: 100 }) ``` -------------------------------- ### Compose Codecs into a Pipeline (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Composes multiple codecs into a single pipeline for encoding and decoding. This example builds a secure codec pipeline using SuperJSON, AES-256-GCM encryption, and Base64URL encoding for URL-safe cursors. ```typescript import { codecPipe, superJsonCodec, createAesCodec, base64UrlCodec } from 'kysely-cursor' // Build a secure codec pipeline: // 1. SuperJSON preserves Date, BigInt, etc. // 2. AES-256-GCM encryption // 3. Base64URL encoding for URL safety const cursorCodec = codecPipe( superJsonCodec, createAesCodec(process.env.PAGINATION_SECRET!), base64UrlCodec ) const paginator = createPaginator({ dialect: PostgresPaginationDialect, cursorCodec }) // Cursors are now encrypted and opaque const result = await paginator.paginate({ query: db.selectFrom('users').select(['id', 'email']), sorts: [{ col: 'id', dir: 'asc' }] as const, limit: 25 }) console.log(result.nextPage) // Example: "v1_abc123...xyz" (encrypted, cannot be decoded without secret) ``` -------------------------------- ### Offset Fallback for Pagination Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Provides an example of using an offset for pagination, which is useful for legacy routes or when numeric offsets are required. This bypasses cursor-based logic for direct row skipping. ```typescript const page3 = await paginator.paginate({ query: postsQ, sorts, limit: 20, cursor: { offset: 40 }, // skip first 40 rows (page index * limit) }) ``` -------------------------------- ### Multi-Column Sort Configuration in Kysely Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Illustrates how to define multi-column sort specifications, including support for nullable leading columns and a required unique final column for tie-breaking. The example uses `createPaginator` with `PostgresPaginationDialect` to set up sorts by `last_login` (descending), `premium_tier` (descending), and `id` (ascending). It explains how null values are handled in descending sorts and ensures consistent sorting across pages. ```typescript import { createPaginator, PostgresPaginationDialect } from 'kysely-cursor' const paginator = createPaginator({ dialect: PostgresPaginationDialect }) // Multi-column sorting with nullable columns const sorts = [ // Nullable leading sorts allowed { col: 'users.last_login', dir: 'desc', output: 'last_login' }, { col: 'users.premium_tier', dir: 'desc', output: 'premium_tier' }, // Final sort MUST be unique and non-nullable (tie-breaker) { col: 'users.id', dir: 'asc', output: 'id' } ] as const const result = await paginator.paginate({ query: db.selectFrom('users').select([ 'id', 'email', 'last_login', 'premium_tier' ]), sorts, limit: 50 }) // Users sorted by: last_login DESC, then premium_tier DESC, then id ASC // NULL last_login values appear last in DESC sort // NULL premium_tier values treated consistently across pages ``` -------------------------------- ### Base64URL Codec for URL Safety (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Encodes UTF-8 strings to URL-safe Base64 format, making cursors safe for use in URLs and HTTP headers. It avoids characters like '+', '/', and '='. This example shows encoding and decoding a JSON string. ```typescript import { base64UrlCodec } from 'kysely-cursor' const original = '{"sig":"abc123","k":{"id":42}}' const encoded = await base64UrlCodec.encode(original) console.log(encoded) // "eyJzaWciOiJhYmMxMjMiLCJrIjp7ImlkIjo0Mn19" // No +, /, or = characters (URL safe) const decoded = await base64UrlCodec.decode(encoded) console.log(decoded) // '{"sig":"abc123","k":{"id":42}}' ``` -------------------------------- ### Stash Codec for External Storage (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Stores cursor payloads in external storage like Redis or a database, returning short UUID keys instead of large tokens. This example uses Redis to store and retrieve cursor data with a one-hour Time-To-Live (TTL). ```typescript import { stashCodec } from 'kysely-cursor' import Redis from 'ioredis' const redis = new Redis(process.env.REDIS_URL) // Create stash implementation const stash = { get: async (key: string) => { const value = await redis.get(`cursor:${key}`) if (!value) throw new Error('Cursor not found or expired') return value }, set: async (key: string, val: string) => { await redis.set(`cursor:${key}`, val, 'EX', 3600) // 1 hour TTL } } const cursorCodec = stashCodec(stash) const paginator = createPaginator({ dialect: PostgresPaginationDialect, cursorCodec }) const result = await paginator.paginate({ query: db.selectFrom('orders').select(['id', 'total', 'created_at']), sorts: [ { col: 'created_at', dir: 'desc' }, { col: 'id', dir: 'desc' } ] as const, limit: 50 }) // nextPage is now just a UUID console.log(result.nextPage) // "550e8400-e29b-41d4-a716-446655440000" // Actual cursor data is stored in Redis under "cursor:550e8400-..." ``` -------------------------------- ### Custom Codec for Stashing Payload Externally Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Shows how to use a custom codec to stash the pagination payload externally, for example, in Redis. This approach results in tokens that resemble random UUIDs, with the actual payload stored separately. ```typescript import { stashCodec } from 'kysely-cursor' const stash = { get: async (key: string) => redis.get(`cursor:${key}`)!, set: async (key: string, val: string) => { await redis.set(`cursor:${key}`, val, { EX: 3600 }) }, } const cursorCodec = stashCodec(stash) // Returned tokens look like random UUIDs; payload is stored in Redis. ``` -------------------------------- ### SuperJSON Codec for Preserving Special Types (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Serializes and deserializes JavaScript values, preserving special types like Date, BigInt, undefined, Map, Set, and RegExp. This example demonstrates encoding and decoding with a Date object, ensuring its type is maintained. ```typescript import { superJsonCodec, base64UrlCodec, codecPipe } from 'kysely-cursor' // SuperJSON preserves type information const codec = codecPipe(superJsonCodec, base64UrlCodec) // Example cursor payload with Date objects const cursorData = { sig: 'abc123', k: { created_at: new Date('2024-01-15T10:30:00Z'), id: 42 } } const encoded = await codec.encode(cursorData) console.log(encoded) // Base64URL string const decoded = await codec.decode(encoded) console.log(decoded.k.created_at instanceof Date) // true console.log(decoded.k.created_at.toISOString()) // "2024-01-15T10:30:00.000Z" ``` -------------------------------- ### Error Handling in Kysely Pagination Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Details structured error handling for pagination operations using `kysely-cursor`. It shows how to catch `PaginationError` exceptions and inspect their `code` property for specific failure reasons like `INVALID_TOKEN`, `INVALID_SORT`, `INVALID_LIMIT`, or `UNEXPECTED_ERROR`. The example provides guidance on responding to different error codes, such as returning appropriate HTTP status codes to clients or logging internal issues. ```typescript import { paginate, PaginationError, PostgresPaginationDialect } from 'kysely-cursor' try { const result = await paginate({ query: db.selectFrom('items').select(['id', 'name']), sorts: [{ col: 'id', dir: 'asc' }] as const, limit: 25, cursor: { nextPage: 'invalid-cursor-token' }, dialect: PostgresPaginationDialect }) } catch (error) { if (error instanceof PaginationError) { console.error('Pagination error:', error.message) console.error('Error code:', error.code) // Error codes: // - INVALID_TOKEN: Malformed or tampered cursor // - INVALID_SORT: Missing or invalid sort configuration // - INVALID_LIMIT: Non-positive or non-integer limit // - UNEXPECTED_ERROR: Internal error if (error.code === 'INVALID_TOKEN') { // Return 400 Bad Request to client return { error: 'Invalid page token', status: 400 } } if (error.code === 'INVALID_LIMIT') { // Return 400 Bad Request return { error: 'Invalid page size', status: 400 } } // Log internal errors if (error.code === 'UNEXPECTED_ERROR') { console.error('Internal error:', error.cause) return { error: 'Internal server error', status: 500 } } } throw error } ``` -------------------------------- ### createPaginator Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Creates a paginator instance with default configurations for dialect and cursor codec. The returned paginator object provides `paginate` and `paginateWithEdges` methods. ```APIDOC ## createPaginator ### Description Creates a paginator instance with default configurations for dialect and cursor codec. The returned paginator object provides `paginate` and `paginateWithEdges` methods. ### Method `createPaginator(options: PaginatorOptions): Paginator` ### Endpoint N/A (This is a function import) ### Parameters #### Request Body - **dialect** (PaginationDialect) - Required - The pagination dialect to use (e.g., `PostgresPaginationDialect`). - **cursorCodec** (optional: Codec) - Optional - The codec to use for encoding/decoding cursors. Defaults to `codecPipe(superJsonCodec, base64UrlCodec)`. ### Request Example ```ts import { createPaginator, type PaginatorOptions, type Paginator } from 'kysely-cursor' const paginator: Paginator = createPaginator({ dialect, // PaginationDialect cursorCodec, // optional: Codec; defaults to SuperJSON+Base64URL }) ``` ### Response #### Success Response (200) - **Paginator** - An object with `paginate` and `paginateWithEdges` methods. ``` -------------------------------- ### Cursor Pagination: Forward and Backward Navigation Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Demonstrates how to implement forward and backward pagination using the kysely-cursor library. It shows setting up sort criteria and fetching subsequent or previous pages based on the cursor. ```typescript const sorts = [ { col: 'posts.published_at', dir: 'desc', output: 'published_at' }, { col: 'posts.id', dir: 'desc', output: 'id' }, ] as const const page1 = await paginator.paginate({ query: postsQ, sorts, limit: 20 }) // forward const page2 = await paginator.paginate({ query: postsQ, sorts, limit: 20, cursor: { nextPage: page1.nextPage! }, }) // backward (internally inverts sorts to walk back) const backToPage1 = await paginator.paginate({ query: postsQ, sorts, limit: 20, cursor: { prevPage: page2.prevPage! }, }) ``` -------------------------------- ### createPaginator Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Factory function to create a reusable paginator instance with configured dialect and cursor codec settings. ```APIDOC ## createPaginator ### Description Factory function that creates a reusable paginator instance with configured dialect and cursor codec settings. ### Method Function Call ### Endpoint N/A (Function) ### Parameters #### Function Parameters - **dialect** (PaginationDialect) - Required - The dialect to use for pagination (e.g., `PostgresPaginationDialect`). - **cursorCodec** (CodecPipe) - Optional - The codec for encrypting and decrypting cursor tokens. Defaults to `codecPipe(superJsonCodec, base64UrlCodec)`. ### Request Example ```typescript import { Kysely, PostgresDialect } from 'kysely' import { Pool } from 'pg' import { createPaginator, PostgresPaginationDialect, codecPipe, superJsonCodec, base64UrlCodec } from 'kysely-cursor' type DB = { users: { id: number email: string created_at: Date } } const db = new Kysely({ dialect: new PostgresDialect({ pool: new Pool({ connectionString: process.env.DATABASE_URL }) }) }) // Create paginator with default codec (SuperJSON + Base64URL) const paginator = createPaginator({ dialect: PostgresPaginationDialect, cursorCodec: codecPipe(superJsonCodec, base64UrlCodec) // optional, this is the default }) // Use the paginator const sorts = [ { col: 'users.created_at', dir: 'desc', output: 'created_at' }, { col: 'users.id', dir: 'desc', output: 'id' } ] as const const result = await paginator.paginate({ query: db.selectFrom('users').select(['id', 'email', 'created_at']), sorts, limit: 25 }) console.log(result.items) // Array of 25 users console.log(result.hasNextPage) // true if more results exist console.log(result.nextPage) // Opaque cursor token for next page ``` ### Response #### Success Response - **paginator** (object) - An object with a `paginate` method. #### Response Example ```json { "items": [ // ... user objects ], "hasNextPage": true, "nextPage": "opaque_cursor_token" } ``` ``` -------------------------------- ### Create a Paginator Instance Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Initializes a paginator instance with specified dialect and optional cursor codec. The returned paginator object provides 'paginate' and 'paginateWithEdges' methods with these defaults applied. ```typescript import { createPaginator, type PaginatorOptions, type Paginator } from 'kysely-cursor' const paginator: Paginator = createPaginator({ dialect, // PaginationDialect cursorCodec, // optional: Codec; defaults to SuperJSON+Base64URL }) ``` -------------------------------- ### Create a Paginator Instance with Kysely Cursor (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Demonstrates creating a reusable paginator instance using `createPaginator`. This function configures the dialect and cursor codec settings, enabling efficient keyset pagination. It requires Kysely and dialect-specific modules from `kysely-cursor`. The output is a paginator object that can be used to fetch paginated results. ```typescript import { Kysely, PostgresDialect } from 'kysely' import { Pool } from 'pg' import { createPaginator, PostgresPaginationDialect, codecPipe, superJsonCodec, base64UrlCodec } from 'kysely-cursor' type DB = { users: { id: number email: string created_at: Date } } const db = new Kysely({ dialect: new PostgresDialect({ pool: new Pool({ connectionString: process.env.DATABASE_URL }) }) }) // Create paginator with default codec (SuperJSON + Base64URL) const paginator = createPaginator({ dialect: PostgresPaginationDialect, cursorCodec: codecPipe(superJsonCodec, base64UrlCodec) // optional, this is the default }) // Use the paginator const sorts = [ { col: 'users.created_at', dir: 'desc', output: 'created_at' }, { col: 'users.id', dir: 'desc', output: 'id' } ] as const const result = await paginator.paginate({ query: db.selectFrom('users').select(['id', 'email', 'created_at']), sorts, limit: 25 }) console.log(result.items) // Array of 25 users console.log(result.hasNextPage) // true if more results exist console.log(result.nextPage) // Opaque cursor token for next page ``` -------------------------------- ### Custom Codec Pipelines for Opaque Tokens Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Illustrates creating custom codec pipelines to make pagination tokens opaque and shorter. This involves chaining codecs like superJson, AES encryption, and base64Url encoding for secure and compact tokens. ```typescript import { codecPipe, superJsonCodec, base64UrlCodec, createAesCodec } from 'kysely-cursor' const cursorCodec = codecPipe( superJsonCodec, // stable serialization (Date, BigInt, etc.) createAesCodec(process.env.PAGINATION_SECRET!), // encrypt base64UrlCodec, // URL‑safe string ) ``` -------------------------------- ### paginate Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Core low-level pagination function that executes a keyset-paginated query and returns items with cursor metadata. ```APIDOC ## paginate ### Description Core low-level pagination function that executes a keyset-paginated query and returns items with cursor metadata. ### Method Function Call ### Endpoint N/A (Function) ### Parameters #### Function Parameters - **query** (KyselyQuery) - Required - The Kysely query to paginate. - **sorts** (Array) - Required - An array of sort definitions for the pagination. - **limit** (number) - Required - The maximum number of items to return per page. - **dialect** (PaginationDialect) - Required - The dialect to use for pagination (e.g., `PostgresPaginationDialect`). - **cursor** (object) - Optional - Cursor object for navigation. Contains `nextPage` or `prevPage` tokens. - **nextPage** (string) - Optional - Token for the next page. - **prevPage** (string) - Optional - Token for the previous page. ### Request Example ```typescript import { paginate, PostgresPaginationDialect } from 'kysely-cursor' const sorts = [ { col: 'posts.published_at', dir: 'desc', output: 'published_at' }, { col: 'posts.id', dir: 'desc', output: 'id' } ] as const // First page const page1 = await paginate({ query: db.selectFrom('posts').select(['id', 'title', 'published_at']), sorts, limit: 20, dialect: PostgresPaginationDialect }) // Navigate forward const page2 = await paginate({ query: db.selectFrom('posts').select(['id', 'title', 'published_at']), sorts, limit: 20, cursor: { nextPage: page1.nextPage! }, dialect: PostgresPaginationDialect }) // Navigate backward const backToPage1 = await paginate({ query: db.selectFrom('posts').select(['id', 'title', 'published_at']), sorts, limit: 20, cursor: { prevPage: page2.prevPage! }, dialect: PostgresPaginationDialect }) ``` ### Response #### Success Response (200) - **items** (Array) - An array of the paginated items. - **startCursor** (string | null) - The cursor of the first item in the result set. - **endCursor** (string | null) - The cursor of the last item in the result set. - **nextPage** (string | null) - An opaque token to fetch the next page. - **prevPage** (string | null) - An opaque token to fetch the previous page. - **hasNextPage** (boolean) - Indicates if there is a next page. - **hasPrevPage** (boolean) - Indicates if there is a previous page. #### Response Example ```json { "items": [ { "id": 1, "title": "Post 1", "published_at": "2023-01-01T10:00:00.000Z" }, { "id": 2, "title": "Post 2", "published_at": "2023-01-01T09:00:00.000Z" } ], "startCursor": "cursor_for_post_1", "endCursor": "cursor_for_post_2", "nextPage": "next_page_token", "prevPage": null, "hasNextPage": true, "hasPrevPage": false } ``` ``` -------------------------------- ### paginateWithEdges Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Extended pagination function that returns results in GraphQL Relay-style edges format with per-item cursors. ```APIDOC ## paginateWithEdges ### Description Extended pagination function that returns results in GraphQL Relay-style edges format with per-item cursors. ### Method Function Call ### Endpoint N/A (Function) ### Parameters #### Function Parameters - **query** (KyselyQuery) - Required - The Kysely query to paginate. - **sorts** (Array) - Required - An array of sort definitions for the pagination. - **limit** (number) - Required - The maximum number of items to return per page. - **dialect** (PaginationDialect) - Required - The dialect to use for pagination (e.g., `PostgresPaginationDialect`). - **cursor** (object) - Optional - Cursor object for navigation. Contains `nextPage` or `prevPage` tokens. - **nextPage** (string) - Optional - Token for the next page. - **prevPage** (string) - Optional - Token for the previous page. ### Request Example ```typescript import { paginateWithEdges, PostgresPaginationDialect } from 'kysely-cursor' const sorts = [ { col: 'products.price', dir: 'desc', output: 'price' }, { col: 'products.id', dir: 'asc', output: 'id' } ] as const const result = await paginateWithEdges({ query: db.selectFrom('products').select(['id', 'name', 'price']), sorts, limit: 10, dialect: PostgresPaginationDialect }) // Each edge contains a node (the item) and a cursor result.edges.forEach(edge => { console.log(edge.node) // { id: 1, name: 'Widget', price: 99.99 } console.log(edge.cursor) // Opaque cursor string for this specific item }) // Page metadata is the same as paginate() console.log({ startCursor: result.startCursor, endCursor: result.endCursor, nextPage: result.nextPage, prevPage: result.prevPage, hasNextPage: result.hasNextPage, hasPrevPage: result.hasPrevPage }) ``` ### Response #### Success Response (200) - **edges** (Array) - An array of edges, where each edge contains a `node` and a `cursor`. - **node** (any) - The actual data item. - **cursor** (string) - The cursor for this specific item. - **startCursor** (string | null) - The cursor of the first item in the result set. - **endCursor** (string | null) - The cursor of the last item in the result set. - **nextPage** (string | null) - An opaque token to fetch the next page. - **prevPage** (string | null) - An opaque token to fetch the previous page. - **hasNextPage** (boolean) - Indicates if there is a next page. - **hasPrevPage** (boolean) - Indicates if there is a previous page. #### Response Example ```json { "edges": [ { "node": { "id": 1, "name": "Widget", "price": 99.99 }, "cursor": "cursor_for_widget_1" }, { "node": { "id": 2, "name": "Gadget", "price": 89.99 }, "cursor": "cursor_for_gadget_2" } ], "startCursor": "cursor_for_widget_1", "endCursor": "cursor_for_gadget_2", "nextPage": "next_page_token", "prevPage": null, "hasNextPage": true, "hasPrevPage": false } ``` ``` -------------------------------- ### Offset Fallback Pagination with Kysely Source: https://context7.com/lukewpc/kysely-cursor/llms.txt Demonstrates legacy offset-based pagination for backward compatibility. It uses `createPaginator` with `PostgresPaginationDialect` to fetch a specific page of results by skipping a calculated number of rows. The output includes the items for the requested page and a boolean indicating if a next page exists. Offset pagination is noted as slower and less stable than cursor-based pagination. ```typescript import { createPaginator, PostgresPaginationDialect } from 'kysely-cursor' const paginator = createPaginator({ dialect: PostgresPaginationDialect }) const sorts = [ { col: 'articles.published_at', dir: 'desc', output: 'published_at' }, { col: 'articles.id', dir: 'desc', output: 'id' } ] as const // Page-based navigation using numeric offsets const page = 3 const limit = 20 const result = await paginator.paginate({ query: db.selectFrom('articles').select(['id', 'title', 'published_at']), sorts, limit, cursor: { offset: (page - 1) * limit } // Skip first 40 rows }) console.log(result.items) // Items 41-60 console.log(result.hasNextPage) // true if more than 60 items exist // Note: Offset pagination is slower and less stable than cursor-based, // but useful for legacy APIs requiring page numbers ``` -------------------------------- ### paginate Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Performs cursor-based pagination on a Kysely query builder. It returns paginated items along with cursor information for navigation. ```APIDOC ## paginate (low-level) ### Description Performs cursor-based pagination on a Kysely query builder. It returns paginated items along with cursor information for navigation. ### Method `paginate(options: PaginateOptions): Promise>` ### Endpoint N/A (This is a function import) ### Parameters #### Request Body - **query** (Kysely SelectQueryBuilder) - Required - The Kysely query builder to paginate. - **sorts** (SortSet) - Required - The set of sorts to apply for pagination. - **limit** (positive integer) - Required - The maximum number of items to return per page. - **cursor** ({nextPage} | {prevPage} | {offset}) - Required - The cursor object specifying the page to retrieve. - **dialect** (PaginationDialect) - Required - The pagination dialect to use. - **cursorCodec** (optional: Codec) - Optional - The codec to use for encoding/decoding cursors. ### Request Example ```ts import { paginate } from 'kysely-cursor' const result = await paginate({ query, sorts, limit, cursor, dialect, cursorCodec, // optional }) ``` ### Response #### Success Response (200) - **items** (T[]) - The array of paginated items. - **startCursor** (string) - Optional - The cursor for the first item in the result set. - **endCursor** (string) - Optional - The cursor for the last item in the result set. - **nextPage** (string) - Optional - A cursor string for fetching the next page. - **prevPage** (string) - Optional - A cursor string for fetching the previous page. - **hasNextPage** (boolean) - Indicates if there is a next page. - **hasPrevPage** (boolean) - Indicates if there is a previous page. #### Response Example ```json { "items": [ { "id": 1, "name": "Item 1" }, { "id": 2, "name": "Item 2" } ], "startCursor": "cursor1", "endCursor": "cursor2", "nextPage": "nextPageCursor", "prevPage": "prevPageCursor", "hasNextPage": true, "hasPrevPage": false } ``` ``` -------------------------------- ### Execute Keyset-Paginated Query with paginate (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt The `paginate` function is the core low-level API for performing keyset pagination. It takes a Kysely query, sorting criteria, and a limit to fetch a page of results. It supports navigating forward and backward using `nextPage` and `prevPage` cursors obtained from previous calls. The function returns detailed pagination metadata, including items, cursors, and page existence booleans. ```typescript import { paginate, PostgresPaginationDialect } from 'kysely-cursor' const sorts = [ { col: 'posts.published_at', dir: 'desc', output: 'published_at' }, { col: 'posts.id', dir: 'desc', output: 'id' } ] as const // First page const page1 = await paginate({ query: db.selectFrom('posts').select(['id', 'title', 'published_at']), sorts, limit: 20, dialect: PostgresPaginationDialect }) // Navigate forward const page2 = await paginate({ query: db.selectFrom('posts').select(['id', 'title', 'published_at']), sorts, limit: 20, cursor: { nextPage: page1.nextPage! }, dialect: PostgresPaginationDialect }) // Navigate backward const backToPage1 = await paginate({ query: db.selectFrom('posts').select(['id', 'title', 'published_at']), sorts, limit: 20, cursor: { prevPage: page2.prevPage! }, dialect: PostgresPaginationDialect }) // Result structure console.log({ items: page1.items, // Array of results startCursor: page1.startCursor, // Cursor of first item endCursor: page1.endCursor, // Cursor of last item nextPage: page1.nextPage, // Token for next page prevPage: page1.prevPage, // Token for previous page hasNextPage: page1.hasNextPage, // Boolean hasPrevPage: page1.hasPrevPage // Boolean }) ``` -------------------------------- ### paginateWithEdges Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Similar to `paginate`, but returns each item along with its associated cursor in an `edges` array. This is useful for scenarios where individual item cursors are needed. ```APIDOC ## paginateWithEdges (low-level) ### Description Similar to `paginate`, but returns each item along with its associated cursor in an `edges` array. This is useful for scenarios where individual item cursors are needed. ### Method `paginateWithEdges(options: PaginateOptions): Promise>` ### Endpoint N/A (This is a function import) ### Parameters #### Request Body - **query** (Kysely SelectQueryBuilder) - Required - The Kysely query builder to paginate. - **sorts** (SortSet) - Required - The set of sorts to apply for pagination. - **limit** (positive integer) - Required - The maximum number of items to return per page. - **cursor** ({nextPage} | {prevPage} | {offset}) - Required - The cursor object specifying the page to retrieve. - **dialect** (PaginationDialect) - Required - The pagination dialect to use. - **cursorCodec** (optional: Codec) - Optional - The codec to use for encoding/decoding cursors. ### Request Example ```ts import { paginateWithEdges } from 'kysely-cursor' const result = await paginateWithEdges({ query, sorts, limit, cursor, dialect, cursorCodec, // optional }) ``` ### Response #### Success Response (200) - **edges** ({node: T, cursor: string}[]) - An array of edges, where each edge contains an item (`node`) and its corresponding cursor. - **startCursor** (string) - Optional - The cursor for the first item in the result set. - **endCursor** (string) - Optional - The cursor for the last item in the result set. - **nextPage** (string) - Optional - A cursor string for fetching the next page. - **prevPage** (string) - Optional - A cursor string for fetching the previous page. - **hasNextPage** (boolean) - Indicates if there is a next page. - **hasPrevPage** (boolean) - Indicates if there is a previous page. #### Response Example ```json { "edges": [ { "node": { "id": 1, "name": "Item 1" }, "cursor": "edgeCursor1" }, { "node": { "id": 2, "name": "Item 2" }, "cursor": "edgeCursor2" } ], "startCursor": "cursor1", "endCursor": "cursor2", "nextPage": "nextPageCursor", "prevPage": "prevPageCursor", "hasNextPage": true, "hasPrevPage": false } ``` ``` -------------------------------- ### Create AES Encryption Codec (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt A factory function that creates an AES-256-GCM encryption codec using scrypt-derived keys for secure cursor encryption. Decryption requires the same secret key; otherwise, it throws an authentication error. ```typescript import { createAesCodec, codecPipe, superJsonCodec } from 'kysely-cursor' // Create encrypted codec with secret key const aesCodec = createAesCodec(process.env.PAGINATION_SECRET!) // Use in pipeline const codec = codecPipe(superJsonCodec, aesCodec) const sensitive = { userId: 123, role: 'admin', timestamp: new Date() } const encrypted = await codec.encode(sensitive) console.log(encrypted) // Base64 string containing: [version][salt][iv][tag][ciphertext] // Decryption requires the same secret const decrypted = await codec.decode(encrypted) console.log(decrypted) // { userId: 123, role: 'admin', timestamp: Date } // Wrong secret or tampered data throws error try { const wrongCodec = createAesCodec('wrong-secret') await wrongCodec.decode(encrypted) // Throws authentication error } catch (error) { console.error('Decryption failed') } ``` -------------------------------- ### Low-Level Paginate Function Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Performs cursor-based pagination on a Kysely query builder. It requires the query, a sort set, a limit, cursor information, and a dialect. The function returns a PaginatedResult object containing items and pagination metadata. ```typescript import { paginate } from 'kysely-cursor' const result = await paginate({ query, // Kysely SelectQueryBuilder sorts, // SortSet limit, // positive integer cursor, // { nextPage } | { prevPage } | { offset } dialect, // PaginationDialect cursorCodec, // optional }) ``` -------------------------------- ### Low-Level PaginateWithEdges Function Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Similar to `paginate`, but returns edges, where each item is associated with its cursor. This is useful for generating individual cursors for each item in the result set. ```typescript import { paginateWithEdges } from 'kysely-cursor' const result = await paginateWithEdges({ query, // Kysely SelectQueryBuilder sorts, // SortSet limit, // positive integer cursor, // { nextPage } | { prevPage } | { offset } dialect, // PaginationDialect cursorCodec, // optional }) ``` -------------------------------- ### Paginate with GraphQL Relay-Style Edges using paginateWithEdges (TypeScript) Source: https://context7.com/lukewpc/kysely-cursor/llms.txt The `paginateWithEdges` function extends the `paginate` functionality by returning results in the standard GraphQL Relay-style edges format. Each edge includes the `node` (the actual data item) and its corresponding `cursor`. This is useful for clients expecting this specific structure for pagination. It requires a Kysely query, sorting criteria, and a limit, similar to `paginate`. ```typescript import { paginateWithEdges, PostgresPaginationDialect } from 'kysely-cursor' const sorts = [ { col: 'products.price', dir: 'desc', output: 'price' }, { col: 'products.id', dir: 'asc', output: 'id' } ] as const const result = await paginateWithEdges({ query: db.selectFrom('products').select(['id', 'name', 'price']), sorts, limit: 10, dialect: PostgresPaginationDialect }) // Each edge contains a node (the item) and a cursor result.edges.forEach(edge => { console.log(edge.node) // { id: 1, name: 'Widget', price: 99.99 } console.log(edge.cursor) // Opaque cursor string for this specific item }) // Page metadata is the same as paginate() console.log({ startCursor: result.startCursor, endCursor: result.endCursor, nextPage: result.nextPage, prevPage: result.prevPage, hasNextPage: result.hasNextPage, hasPrevPage: result.hasPrevPage }) ``` -------------------------------- ### PaginatedResult Type Definition Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Defines the structure of the return type for the `paginate` function. It includes the paginated items and boolean flags indicating the presence of next and previous pages, along with cursor strings. ```typescript export type PaginatedResult = { items: T[] startCursor?: string endCursor?: string nextPage?: string prevPage?: string hasNextPage: boolean hasPrevPage: boolean } ``` -------------------------------- ### PaginationError Structure Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Defines the structure of `PaginationError` thrown by the library for operational errors. It includes a message, an error code, and an optional cause, intended to be treated as a 400 Bad Request. ```typescript { message: string code: ErrorCode cause?: Error } ``` -------------------------------- ### Define Sort Set for Unique Row Identification Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Defines an ordered set of sorts to uniquely identify rows for pagination. The final sort must be non-nullable and unique to act as a tie-breaker. 'dir' specifies the sort direction (asc/desc), 'col' is the field to sort by, and 'output' is the field name in the outputted rows. ```typescript const sorts = [ { col: 'users.created_at', dir: 'desc', output: 'created_at' }, { col: 'users.id', dir: 'desc', output: 'id' }, // final non‑nullable & unique key ] as const ``` -------------------------------- ### PaginatedResultWithEdges Type Definition Source: https://github.com/lukewpc/kysely-cursor/blob/main/README.md Defines the structure of the return type for the `paginateWithEdges` function. It includes an array of edges, where each edge contains a node (the item) and its corresponding cursor. ```typescript export type PaginatedResultWithEdges = { edges: { node: T cursor: string }[] startCursor?: string endCursor?: string nextPage?: string prevPage?: string hasNextPage: boolean hasPrevPage: boolean } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.