PageSourceSearch

https://q01-platform.github.io/assets/js/db80ffa6.b15016cb.js

js q01-platform.github.io collected 2026-10-03 09:48:48 UTC 25,058 bytes, 1 lines download raw bytes

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.