Có hai chiến lược: offset-based (limit/offset) và cursor-based. GraphQL không có phân trang sẵn, nhưng chuẩn cộng đồng là spec Relay Connection, chính thức hóa phân trang bằng cursor.
Có hai chiến lược: offset-based (limit/offset) và cursor-based. GraphQL không có phân trang sẵn, nhưng chuẩn cộng đồng là spec Relay Connection, chính thức hóa phân trang bằng cursor.
items(limit: 10, offset: 20)) đơn giản nhưng hỏng với dữ liệu sống: nếu một hàng được chèn trong lúc user lật trang, các phần tử dịch chuyển và bạn thấy trùng lặp hoặc bỏ sót. Nó cũng chậm ở offset sâu (DB vẫn phải quét các hàng bị bỏ qua).Một connection bọc danh sách trong edges (mỗi cái có cursor và node) cùng pageInfo:
type Query {
posts(first: Int!, after: String): PostConnection! # first = kích thước trang, after = cursor
}
type PostConnection {
edges: [PostEdge!]!
pageInfo: PageInfo!
}
type PostEdge {
cursor: String! # token mờ cho vị trí của node NÀY
node: Post! # phần tử thực sự
}
type PageInfo {
endCursor: String # đưa lại giá trị này làm 'after' cho trang kế tiếp
hasNextPage: Boolean! # để client biết khi nào dừng
}
// resolver: lấy dư MỘT hàng để tính hasNextPage rẻ tiền
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 },
};
Cursor là mờ (opaque) — thường là base64 của khóa sắp xếp — nên client coi nó là một token, không phải một con số.
Chọn offset hay cursor cho thấy bạn hiểu tính nhất quán khi có ghi đồng thời và hiệu năng ở trang sâu. Biết hình dạng Relay connection rất quan trọng vì client Apollo/Relay có sẵn cơ chế xử lý cache cho nó.
Thư viện câu hỏi phỏng vấn IT với đáp án chi tiết — từ Junior đến Senior.
Ủng hộ