从Prisma1迁移至Prisma2:保留Connection分页格式的方案咨询
usersConnection Pagination to Prisma 2 (Keeping Legacy Schema Structure) Great question—since Prisma 2 (now just "Prisma") doesn’t auto-generate the Relay-style connection structures that Prisma 1 did, you’ll need to manually replicate that schema shape in your GraphQL layer and wrap Prisma’s native pagination API to match the old structure. The good news is you don’t need to fully rebuild user objects, and there’s a clean, maintainable way to do this without breaking your frontend.
Step 1: Keep Your Legacy GraphQL Schema Intact
First, retain the exact type definitions your frontend depends on (add the missing PageInfo type if you haven’t already):
"""A connection to a list of items.""" type UserConnection { """Information to aid in pagination.""" pageInfo: PageInfo! """A list of edges.""" edges: [UserEdge]! aggregate: AggregateUser! } """An edge in a connection.""" type UserEdge { """The item at the end of the edge.""" node: User! """A cursor for use in pagination.""" cursor: String! } type AggregateUser { count: Int! } """Pagination metadata""" type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String }
Step 2: Wrap Prisma’s Native Pagination in Resolvers
Prisma’s findMany supports both offset-based (skip/take) and cursor-based pagination out of the box. You’ll write resolver logic to translate the Relay-style connection arguments (first, last, after, before) into Prisma-compatible queries, then map the results back to your UserConnection structure.
Here’s a practical TypeScript example for the usersConnection resolver:
Helper Functions for Cursor Encoding/Decoding
Prisma 1 typically used base64-encoded IDs as cursors—match that behavior to keep frontend compatibility:
// Encode a user ID into a cursor string const encodeCursor = (id: string): string => Buffer.from(id).toString('base64'); // Decode a cursor string back to a user ID const decodeCursor = (cursor: string): string => Buffer.from(cursor, 'base64').toString('utf-8');
Main Resolver Logic
async function usersConnection(_, args, context) { const { first, last, after, before } = args; const prismaQuery = {}; let hasNextPage = false; let hasPreviousPage = false; // Handle cursor-based pagination (after/before) if (after) { prismaQuery.cursor = { id: decodeCursor(after) }; prismaQuery.skip = 1; // Skip the cursor itself } if (before) { prismaQuery.cursor = { id: decodeCursor(before) }; prismaQuery.skip = 1; prismaQuery.take = -last; // Fetch items before the cursor (reverse order) } // Fetch one extra item to detect if there's a next/previous page if (first) { prismaQuery.take = first + 1; } else if (last) { prismaQuery.take = -last - 1; } // Fetch users from Prisma let users = await context.prisma.user.findMany(prismaQuery); // Adjust results to match connection rules if (first && users.length > first) { hasNextPage = true; users = users.slice(0, first); // Remove the extra item we fetched } if (last) { if (users.length > last) { hasPreviousPage = true; users = users.slice(1).reverse(); // Remove extra item and restore correct order } else { users = users.reverse(); } } // Build edges and page info const edges = users.map(user => ({ node: user, // Use Prisma's user object directly—no need to rebuild! cursor: encodeCursor(user.id) })); const pageInfo = { hasNextPage, hasPreviousPage, startCursor: edges[0]?.cursor || null, endCursor: edges[edges.length - 1]?.cursor || null }; // Get aggregate count const aggregate = { count: await context.prisma.user.count() }; return { pageInfo, edges, aggregate }; }
Do You Need to Rebuild User Objects?
Nope! Prisma returns fully populated User objects that you can directly use as the node field in UserEdge. The only extra work is adding the cursor field (which is just the user’s ID encoded as base64).
Optimal Improvements
- Create a generic connection helper: If you have other models that need connection-style pagination, extract the core logic into a reusable function (e.g.,
createConnectionResolver(model, args)). - Add input validation: Ensure conflicting arguments (like
firstandlastused together) are handled gracefully to avoid errors. - Reuse Prisma’s cursor support: Prisma supports cursor pagination on any unique field, so if you used something other than
idfor cursors in Prisma 1, adjust the cursor logic to match that field.
内容的提问来源于stack exchange,提问作者Alan

