1"use strict";(self.webpackChunkq_01_docs=self.webpackChunkq_01_docs||[]).push([[2744],{3756:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>a,metadata:()=>o,toc:()=>c});var r=s(7624),t=s(2172);const a={title:"Pagination",sidebar_label:"Pagination",sidebar_position:4,description:"Efficient data pagination with $num_rows and $offset parameters"},i="Pagination",o={id:"products/map.q01.io/core-api-platform-v4/query-patterns/pagination",title:"Pagination",description:"Efficient data pagination with $num_rows and $offset parameters",source:"@site/docs/products/map.q01.io/core-api-platform-v4/03-query-patterns/pagination.md",sourceDirName:"products/map.q01.io/core-api-platform-v4/03-query-patterns",slug:"/products/map.q01.io/core-api-platform-v4/query-patterns/pagination",permalink:"/docs/products/map.q01.io/core-api-platform-v4/query-patterns/pagination",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{title:"Pagination",sidebar_label:"Pagination",sidebar_position:4,description:"Efficient data pagination with $num_rows and $offset parameters"},sidebar:"mapSidebar",previous:{title:"Filtering",permalink:"/docs/products/map.q01.io/core-api-platform-v4/query-patterns/filtering"},next:{title:"Field Retrieval",permalink:"/docs/products/map.q01.io/core-api-platform-v4/query-patterns/field-retrieval"}},l={},c=[{value:"Overview",id:"overview",level:2},{value:"Basic Pagination",id:"basic-pagination",level:2},{value:"$num_rows Parameter",id:"num_rows-parameter",level:3},{value:"$offset Parameter",id:"offset-parameter",level:3},{value:"Pagination Formula",id:"pagination-formula",level:3},{value:"Complete Pagination Example",id:"complete-pagination-example",level:2},{value:"Request Sequence",id:"request-sequence",level:3},{value:"Getting Total Count",id:"getting-total-count",level:2},{value:"Count Total Records",id:"count-total-records",level:3},{value:"JavaScript Implementation",id:"javascript-implementation",level:2},{value:"Paginator Class",id:"paginator-class",level:3},{value:"React Pagination Component",id:"react-pagination-component",level:3},{value:"Performance Considerations",id:"performance-considerations",level:2},{value:"Page Size Selection",id:"page-size-selection",level:3},{value:"Deep Pagination Problem",id:"deep-pagination-problem",level:3},{value:"Consistent Sorting",id:"consistent-sorting",level:3},{value:"Cursor-Based Pagination (Alternative)",id:"cursor-based-pagination-alternative",level:2},{value:"Using ID Cursor",id:"using-id-cursor",level:3},{value:"Best Practices",id:"best-practices",level:2},{value:"\u2705 DO:",id:"-do",level:3},{value:"\u274c DON'T:",id:"-dont",level:3},{value:"Common Mistakes",id:"common-mistakes",level:2},{value:"Mistake 1: Forgetting to Calculate $offset",id:"mistake-1-forgetting-to-calculate-offset",level:3},{value:"Mistake 2: Inconsistent Sorting",id:"mistake-2-inconsistent-sorting",level:3},{value:"Mistake 3: Not Handling Empty Pages",id:"mistake-3-not-handling-empty-pages",level:3},{value:"Summary",id:"summary",level:2},{value:"Related Concepts",id:"related-concepts",level:2}];function d(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,t.M)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"pagination",children:"Pagination"}),"\n",(0,r.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsxs)(n.p,{children:["Pagination divides large result sets into manageable pages. Core APIs use ",(0,r.jsx)(n.strong,{children:"offset-based pagination"})," with ",(0,r.jsx)(n.code,{children:"$num_rows"})," (page size) and ",(0,r.jsx)(n.code,{children:"$offset"})," (offset) parameters."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Benefits:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Reduces response size and transfer time"}),"\n",(0,r.jsx)(n.li,{children:"Improves perceived performance"}),"\n",(0,r.jsx)(n.li,{children:"Enables progressive loading"}),"\n",(0,r.jsx)(n.li,{children:"Prevents browser/client memory issues"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"basic-pagination",children:"Basic Pagination"}),"\n",(0,r.jsx)(n.h3,{id:"num_rows-parameter",children:"$num_rows Parameter"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Limits number of results returned:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"GET /api/v4/core/PRD?source=productList&\\\n $num_rows=25\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Response:"})," First 25 records only."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Default:"})," If $num_rows not specified, default is 100 records."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Maximum:"})," 1000 records per request (enforced by Core APIs)."]}),"\n",(0,r.jsx)(n.h3,{id:"offset-parameter",children:"$offset Parameter"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Skips first N results:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"GET /api/v4/core/PRD?source=productList&\\\n $num_rows=25&\\\n $offset=50\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Response:"})," Records 51-75 (skips first 50, returns next 25)."]}),"\n",(0,r.jsx)(n.h3,{id:"pagination-formula",children:"Pagination Formula"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Calculate $offset from page number:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"$offset = (pageNumber - 1) * pageSize\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Examples:"})}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Page"}),(0,r.jsx)(n.th,{children:"Page Size"}),(0,r.jsx)(n.th,{children:"$offset"}),(0,r.jsx)(n.th,{children:"$num_rows"}),(0,r.jsx)(n.th,{children:"Records Returned"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"1"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"0"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"1-25"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"2"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"26-50"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"3"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"50"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"51-75"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"10"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"225"}),(0,r.jsx)(n.td,{children:"25"}),(0,r.jsx)(n.td,{children:"226-250"})]})]})]}),"\n",(0,r.jsx)(n.h2,{id:"complete-pagination-example",children:"Complete Pagination Example"}),"\n",(0,r.jsx)(n.h3,{id:"request-sequence",children:"Request Sequence"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Page 1:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"GET /api/v4/core/PRD?source=productList&\\\n $num_rows=25&\\\n $offset=0&\\\n $order=XPRD01 ASC\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Page 2:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"GET /api/v4/core/PRD?source=productList&\\\n $num_rows=25&\\\n $offset=25&\\\n $order=XPRD01 ASC\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Page 3:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"GET /api/v4/core/PRD?source=productList&\\\n $num_rows=25&\\\n $offset=50&\\\n $order=XPRD01 ASC\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Important:"})," Always include same ",(0,r.jsx)(n.code,{children:"$order"})," to ensure consistent pagination."]}),"\n",(0,r.jsx)(n.h2,{id:"getting-total-count",children:"Getting Total Count"}),"\n",(0,r.jsx)(n.h3,{id:"count-total-records",children:"Count Total Records"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Use $count parameter:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"GET /api/v4/core/PRD?source=productList&\\\n $count=true&\\\n $num_rows=25\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Response includes count:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n "data": [...],\n "count": 1250\n}\n'})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Calculate total pages:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"const totalPages = Math.ceil(count / pageSize);\n// 1250 records / 25 per page = 50 pages\n"})}),"\n",(0,r.jsx)(n.h2,{id:"javascript-implementation",children:"JavaScript Implementation"}),"\n",(0,r.jsx)(n.h3,{id:"paginator-class",children:"Paginator Class"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"class Paginator {\n constructor(apiBase, token, dimension, source) {\n this.apiBase = apiBase;\n this.token = token;\n this.dimension = dimension;\n this.source = source;\n this.pageSize = 25;\n this.currentPage = 1;\n this.totalCount = null;\n }\n\n async fetchPage(pageNumber, options = {}) {\n const skip = (pageNumber - 1) * this.pageSize;\n\n const url = new URL(`${this.apiBase}/api/v4/core/${this.dimension}`);\n url.searchParams.append('source', this.source);\n url.searchParams.append('$num_rows', this.pageSize);\n url.searchParams.append('$offset', skip);\n\n // Include count on first page\n if (pageNumber === 1) {\n url.searchParams.append('$count', 'true');\n }\n\n // Add sorting\n if (options.$order) {\n url.searchParams.append('$order', options.$order);\n }
1\n\n // Add filtering\n if (options.$filter) {\n url.searchParams.append('$filter', options.$filter);\n }\n\n // Add field selection\n if (options.$select) {\n url.searchParams.append('$select', options.$select);\n }\n\n const response = await fetch(url, {\n headers: { 'Authorization': `Bearer ${this.token}` }\n });\n\n if (!response.ok) {\n throw new Error('Pagination request failed');\n }\n\n const result = await response.json();\n\n // Store total count from first page\n if (result.count !== undefined) {\n this.totalCount = result.count;\n }\n\n this.currentPage = pageNumber;\n\n return {\n data: result.data,\n page: pageNumber,\n pageSize: this.pageSize,\n totalCount: this.totalCount,\n totalPages: this.totalCount ? Math.ceil(this.totalCount / this.pageSize) : null,\n hasNext: this.totalCount ? (pageNumber * this.pageSize) < this.totalCount : true,\n hasPrevious: pageNumber > 1\n };\n }\n\n async nextPage(options = {}) {\n return this.fetchPage(this.currentPage + 1, options);\n }\n\n async previousPage(options = {}) {\n if (this.currentPage > 1) {\n return this.fetchPage(this.currentPage - 1, options);\n }\n throw new Error('Already on first page');\n }\n\n async firstPage(options = {}) {\n return this.fetchPage(1, options);\n }\n\n async lastPage(options = {}) {\n if (!this.totalCount) {\n throw new Error('Total count not available');\n }\n const lastPage = Math.ceil(this.totalCount / this.pageSize);\n return this.fetchPage(lastPage, options);\n }\n}\n\n// Usage\nconst paginator = new Paginator(apiBase, token, 'PRD', 'productList');\n\n// Fetch first page\nconst page1 = await paginator.fetchPage(1, {\n $order: 'XPRD01 ASC',\n $select: 'PRD_ID,XPRD01,XPRD02'\n});\n\nconsole.log(`Page 1 of ${page1.totalPages}`);\nconsole.log(`Showing records 1-${page1.data.length} of ${page1.totalCount}`);\n\n// Fetch next page\nconst page2 = await paginator.nextPage({\n $order: 'XPRD01 ASC',\n $select: 'PRD_ID,XPRD01,XPRD02'\n});\n"})}),"\n",(0,r.jsx)(n.h3,{id:"react-pagination-component",children:"React Pagination Component"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"function ProductList() {\n const [products, setProducts] = useState([]);\n const [currentPage, setCurrentPage] = useState(1);\n const [totalPages, setTotalPages] = useState(0);\n const [loading, setLoading] = useState(false);\n const pageSize = 25;\n\n async function loadPage(pageNumber) {\n setLoading(true);\n try {\n const skip = (pageNumber - 1) * pageSize;\n const url = new URL(`${apiBase}/api/v4/core/PRD`);\n url.searchParams.append('source', 'productList');\n url.searchParams.append('$num_rows', pageSize);\n url.searchParams.append('$offset', skip);\n url.searchParams.append('$order', 'XPRD01 ASC');\n url.searchParams.append('$select', 'PRD_ID,XPRD01,XPRD02');\n\n if (pageNumber === 1) {\n url.searchParams.append('$count', 'true');\n }\n\n const response = await fetch(url, {\n headers: { 'Authorization': `Bearer ${token}` }\n });\n\n const result = await response.json();\n setProducts(result.data);\n setCurrentPage(pageNumber);\n\n if (result.count) {\n setTotalPages(Math.ceil(result.count / pageSize));\n }\n } finally {\n setLoading(false);\n }\n }\n\n useEffect(() => {\n loadPage(1);\n }, []);\n\n return (\n <div>\n {loading ? (\n <div>Loading...</div>\n ) : (\n <>\n <ul>\n {products.map(p => (\n <li key={p.PRD_ID}>{p.XPRD01} - ${p.XPRD02}</li>\n ))}\n </ul>\n\n <div className=\"pagination\">\n <button\n disabled={currentPage === 1}\n onClick={() => loadPage(currentPage - 1)}\n >\n Previous\n </button>\n\n <span>Page {currentPage} of {totalPages}</span>\n\n <button\n disabled={currentPage === totalPages}\n onClick={() => loadPage(currentPage + 1)}\n >\n Next\n </button>\n </div>\n </>\n )}\n </div>\n );\n}\n"})}),"\n",(0,r.jsx)(n.h2,{id:"performance-considerations",children:"Performance Considerations"}),"\n",(0,r.jsx)(n.h3,{id:"page-size-selection",children:"Page Size Selection"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Page Size"}),(0,r.jsx)(n.th,{children:"Use Case"}),(0,r.jsx)(n.th,{children:"Performance"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"10-25"}),(0,r.jsx)(n.td,{children:"Mobile apps, autocomplete"}),(0,r.jsx)(n.td,{children:"Fast, low bandwidth"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"25-50"}),(0,r.jsx)(n.td,{children:"Desktop list views"}),(0,r.jsx)(n.td,{children:"Good balance"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"50-100"}),(0,r.jsx)(n.td,{children:"Admin tables, data grids"}),(0,r.jsx)(n.td,{children:"Acceptable"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"100-500"}),(0,r.jsx)(n.td,{children:"Bulk operations, exports"}),(0,r.jsx)(n.td,{children:"Slower, use carefully"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"500-1000"}
1),(0,r.jsx)(n.td,{children:"Background processing only"}),(0,r.jsx)(n.td,{children:"Slow, avoid in UI"})]})]})]}),"\n",(0,r.jsx)(n.h3,{id:"deep-pagination-problem",children:"Deep Pagination Problem"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Performance degrades with high $offset values:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# Fast - skipping 0 records\n$offset=0&$num_rows=25\n\n# Slower - skipping 10,000 records\n$offset=10000&$num_rows=25\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Why?"})," Database must scan and skip 10,000 rows before returning results."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Solution:"})," Limit maximum page number or use cursor-based pagination."]}),"\n",(0,r.jsx)(n.h3,{id:"consistent-sorting",children:"Consistent Sorting"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Always specify $order for pagination:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u2705 Good - consistent order\n$num_rows=25&$offset=0&$order=XPRD03 ASC\n$num_rows=25&$offset=25&$order=XPRD03 ASC\n\n# \u274c Bad - unpredictable order\n$num_rows=25&$offset=0 # No $order\n$num_rows=25&$offset=25 # Results may duplicate or skip\n"})}),"\n",(0,r.jsx)(n.h2,{id:"cursor-based-pagination-alternative",children:"Cursor-Based Pagination (Alternative)"}),"\n",(0,r.jsx)(n.h3,{id:"using-id-cursor",children:"Using ID Cursor"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"More efficient for deep pagination:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# Page 1 - get first 25\nGET /api/v4/core/PRD?source=productList&\\\n $num_rows=25&\\\n $order=PRD_ID ASC\n\n# Page 2 - get next 25 after last ID\nGET /api/v4/core/PRD?source=productList&\\\n $filter=PRD_ID gt 25&\\\n $num_rows=25&\\\n $order=PRD_ID ASC\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Advantages:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Consistent results even if data changes"}),"\n",(0,r.jsx)(n.li,{children:"Better performance for deep pagination"}),"\n",(0,r.jsx)(n.li,{children:"No duplicate/missing records"}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Disadvantages:"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Cannot jump to arbitrary page"}),"\n",(0,r.jsx)(n.li,{children:"More complex implementation"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"best-practices",children:"Best Practices"}),"\n",(0,r.jsx)(n.h3,{id:"-do",children:"\u2705 DO:"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Always use pagination for lists:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u2705 Good\n$num_rows=25&$offset=0\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Include $order for consistency:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u2705 Good\n$num_rows=25&$offset=0&$order=XPRD01 ASC\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Use $count on first page only:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"// \u2705 Good\nif (page === 1) {\n url.searchParams.append('$count', 'true');\n}\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Limit page size for mobile:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u2705 Good for mobile\n$num_rows=20\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Cache total count:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"// \u2705 Good - don't fetch count every page\nif (!this.totalCount) {\n url.searchParams.append('$count', 'true');\n}\n"})}),"\n",(0,r.jsx)(n.h3,{id:"-dont",children:"\u274c DON'T:"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Don't fetch all records without pagination:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u274c Bad - fetches 10,000 records\nGET /api/v4/core/PRD?source=productList\
1n\n# \u2705 Good\nGET /api/v4/core/PRD?source=productList&$num_rows=25\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Don't paginate without sorting:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u274c Bad - inconsistent results\n$num_rows=25&$offset=25\n\n# \u2705 Good\n$num_rows=25&$offset=25&$order=PRD_ID ASC\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Don't use huge page sizes:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u274c Bad - slow, timeout risk\n$num_rows=5000\n\n# \u2705 Good\n$num_rows=100\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Don't request count on every page:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"# \u274c Bad - unnecessary overhead\n$count=true # on every page\n\n# \u2705 Good\n$count=true # only on page 1\n"})}),"\n",(0,r.jsx)(n.h2,{id:"common-mistakes",children:"Common Mistakes"}),"\n",(0,r.jsx)(n.h3,{id:"mistake-1-forgetting-to-calculate-offset",children:"Mistake 1: Forgetting to Calculate $offset"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"// \u274c Wrong - always $offset=0\nfunction loadPage(page) {\n return fetch(`${apiBase}/api/v4/core/PRD?$num_rows=25&$offset=0`);\n}\n\n// \u2705 Correct\nfunction loadPage(page) {\n const skip = (page - 1) * 25;\n return fetch(`${apiBase}/api/v4/core/PRD?$num_rows=25&$offset=${skip}`);\n}\n"})}),"\n",(0,r.jsx)(n.h3,{id:"mistake-2-inconsistent-sorting",children:"Mistake 2: Inconsistent Sorting"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"// \u274c Wrong - different sort on each page\npage1: $order=XPRD01 ASC\npage2: $order=XPRD02 DESC // Changed sort!\n\n// \u2705 Correct - same sort on all pages\npage1: $order=XPRD01 ASC\npage2: $order=XPRD01 ASC\n"})}),"\n",(0,r.jsx)(n.h3,{id:"mistake-3-not-handling-empty-pages",children:"Mistake 3: Not Handling Empty Pages"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"// \u274c Wrong - no empty check\nconst page = await fetchPage(100);\nrenderProducts(page.data); // Might be empty array\n\n// \u2705 Correct\nconst page = await fetchPage(100);\nif (page.data.length === 0) {\n showEmptyState();\n} else {\n renderProducts(page.data);\n}\n"})}),"\n",(0,r.jsx)(n.h2,{id:"summary",children:"Summary"}),"\n",(0,r.jsx)(n.p,{children:"Pagination is essential for performance:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\u2705 Use ",(0,r.jsx)(n.code,{children:"$num_rows"})," to limit page size (default 100, max 1000)"]}),"\n",(0,r.jsxs)(n.li,{children:["\u2705 Use ",(0,r.jsx)(n.code,{children:"$offset"})," to offset results ($offset = (page - 1) * pageSize)"]}),"\n",(0,r.jsxs)(n.li,{children:["\u2705 Always include ",(0,r.jsx)(n.code,{children:"$order"})," for consistent results"]}),"\n",(0,r.jsxs)(n.li,{children:["\u2705 Use ",(0,r.jsx)(n.code,{children:"$count=true"})," on first page only"]}),"\n",(0,r.jsx)(n.li,{children:"\u2705 Choose appropriate page size (25-50 for UI)"}),"\n",(0,r.jsx)(n.li,{children:"\u2705 Cache total count to avoid repeated queries"}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Key Takeaways:"})}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsx)(n.li,{children:"Never fetch large datasets without pagination"}),"\n",(0,r.jsx)(n.li,{children:"Always sort for consistent page results"}),"\n",(0,r.jsx)(n.li,{children:"Calculate $offset correctly from page number"}),"\n",(0,r.jsx)(n.li,{children:"Limit page size for mobile apps (10-20)"}),"\n",(0,r.jsx)(n.li,{children:"Performance degrades with deep pagination (high $offset)"}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Next:"})," ",(0,r.jsx)(n.a,{href:"/docs/products/map.q01.io/core-api-platform-v4/query-patterns/sorting-patterns",children:"Sorting Patterns \u2192"})," to learn advanced sorting strategies."]}),"\n",(0,r.jsx)(n.h2,{id:"related-concepts",children:"Related Concepts"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/products/map.q01.io/core-api-platform-v4/query-patterns/basic-queries",children:"Basic Quer
1ies"})," - Query fundamentals"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/products/map.q01.io/core-api-platform-v4/query-patterns/field-projection",children:"Field Projection"})," - Reducing response size"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/products/map.q01.io/core-api-platform-v4/query-patterns/query-performance",children:"Query Performance"})," - Performance optimization"]}),"\n"]})]})}function p(e={}){const{wrapper:n}={...(0,t.M)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},2172:(e,n,s)=>{s.d(n,{I:()=>o,M:()=>i});var r=s(1504);const t={},a=r.createContext(t);function i(e){const n=r.useContext(a);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:i(e.components),r.createElement(a.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.