Две стратегии: offset-based (limit/offset) и cursor-based. В GraphQL нет встроенной пагинации, но стандарт сообщества — спецификация Relay Connection, формализующая курсорную пагинацию.
Две стратегии: offset-based (limit/offset) и cursor-based. В GraphQL нет встроенной пагинации, но стандарт сообщества — спецификация Relay Connection, формализующая курсорную пагинацию.
items(limit: 10, offset: 20)) прост, но ломается на живых данных: если строка вставляется, пока пользователь листает, элементы смещаются, и вы видите дубликаты или пропуски. Он также замедляется на глубоких offset (БД всё равно сканирует пропущенные строки).Connection оборачивает список в edges (каждый с cursor и node) плюс pageInfo:
type Query {
posts(first: Int!, after: String): PostConnection! # first = размер страницы, after = курсор
}
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
}
type PostEdge {
cursor: String! # непрозрачный токен позиции ЭТОГО node
node: Post! # сам элемент
}
type PageInfo {
endCursor: String # передайте обратно как 'after' для следующей страницы
hasNextPage: Boolean! # чтобы клиент знал, когда остановиться
}
// resolver: получаем ОДНУ лишнюю строку, чтобы дёшево вычислить hasNextPage
const rows = await db.posts.find({ after: args.after, limit: args.first + 1 });
const hasNextPage = rows.length > args.first;
const page = rows.slice(0, args.first);
return {
edges: page.map((p) => ({ node: p, cursor: encodeCursor(p.id) })),
pageInfo: { endCursor: page.at(-1) && encodeCursor(page.at(-1).id), hasNextPage },
};
Курсор непрозрачен — обычно это base64 от ключа сортировки — поэтому клиенты трактуют его как токен, а не как число.
Выбор offset или cursor показывает, что вы понимаете согласованность при конкурентных записях и производительность на глубоких страницах. Знание формы Relay connection важно, потому что клиенты Apollo/Relay имеют встроенную обработку кэша для неё.
Библиотека вопросов для IT-собеседований с подробными ответами — от Junior до Senior.
Поддержать