Skip to content

Pagination#2406

Open
moubctez wants to merge 16 commits intodevfrom
pagination
Open

Pagination#2406
moubctez wants to merge 16 commits intodevfrom
pagination

Conversation

@moubctez
Copy link
Contributor

No description provided.

@moubctez moubctez requested a review from Copilot March 19, 2026 10:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces paginated API responses for several “list” endpoints (users, groups, OAuth clients, ACL rules, network devices, activity log, location stats) and updates the web client to consume the new paginated shape, including a helper to fetch all pages where the UI still needs full lists.

Changes:

  • Add shared pagination primitives on the backend (PaginationParams, PaginatedApiResponse) and wire them into multiple handlers.
  • Add web-side pagination helpers (fetchPage, fetchAllPages) and migrate selected API methods/query options away from resp.data selectors.
  • Extend the Model derive to generate all_paginated(...) and count() methods and refresh sqlx offline query metadata.

Reviewed changes

Copilot reviewed 40 out of 101 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
web/src/shared/query.ts Removes select: resp.data where API now returns raw arrays.
web/src/shared/api/types.ts Removes GroupsResponse; renames pagination total_pagers -> total_pages.
web/src/shared/api/pagination.ts Adds fetchPage + fetchAllPages helpers for paginated endpoints.
web/src/shared/api/api.ts Switches selected list endpoints to fetchAllPages / fetchPage.
web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx Adjusts groups fetch to new groups API return type.
web/src/pages/RulesPage/tabs/RulesPendingTab.tsx Updates query select to work with array result (not Axios response).
web/src/pages/RulesPage/tabs/RulesDeployedTab.tsx Updates query select to work with array result (not Axios response).
web/src/pages/EditLocationPage/EditLocationPage.tsx Updates groups query select to map string array directly.
web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx Removes select since groups query now returns string[].
crates/model_derive/src/lib.rs Adds all_paginated, count; refactors attribute parsing.
crates/defguard_version/src/lib.rs Minor Display impl refactor (write_str).
crates/defguard_proto/src/lib.rs Minor Display impl refactor (write_str).
crates/defguard_gateway_manager/src/handler.rs Simplifies disconnect-notification condition block.
crates/defguard_core/src/openapi.rs Removes Groups schema usage from OpenAPI components.
crates/defguard_core/src/handlers/wireguard.rs Adapts network counting to new count() return type.
crates/defguard_core/src/handlers/user.rs Makes /user list paginated; adjusts visibility to pub(crate).
crates/defguard_core/src/handlers/pagination.rs Introduces new pagination params/response implementation.
crates/defguard_core/src/handlers/openid_clients.rs Makes OAuth client list paginated; adjusts visibility.
crates/defguard_core/src/handlers/network_devices.rs Makes network device list paginated; adds count + paged query.
crates/defguard_core/src/handlers/mod.rs Removes DEFAULT_API_PAGE_SIZE constant.
crates/defguard_core/src/handlers/location_stats.rs Switches location stats endpoints to new pagination params/meta flow.
crates/defguard_core/src/handlers/group.rs Makes groups list paginated; removes Groups wrapper type.
crates/defguard_core/src/handlers/activity_log.rs Switches activity log pagination to new params/meta flow.
crates/defguard_core/src/grpc/proxy/client_mfa.rs Uses {device} formatting shorthand.
crates/defguard_core/src/enterprise/snat/handlers.rs OpenAPI response body syntax tweak ([T]).
crates/defguard_core/src/enterprise/handlers/acl/destination.rs OpenAPI response body syntax tweak ([T]).
crates/defguard_core/src/enterprise/handlers/acl/alias.rs OpenAPI response body syntax tweak ([T]).
crates/defguard_core/src/enterprise/handlers/acl.rs Makes ACL rules list paginated; adds pagination params.
crates/defguard_core/src/enterprise/db/models/openid_provider.rs Minor Display impl refactor (write_str).
crates/defguard_core/src/enterprise/db/models/acl.rs Minor Display impl refactor (write_str).
crates/defguard_common/src/hex.rs Simplifies std fmt imports; keeps Display behavior.
crates/defguard_common/src/db/models/wizard.rs Minor Display impl refactor (write_str).
crates/defguard_common/src/db/models/wireguard.rs Removes bespoke count; updates connected-user query to accept limit/offset.
crates/defguard_common/src/db/models/user.rs Minor Display impl refactor (write_str).
crates/defguard_common/src/db/models/proxy.rs Minor Display impl refactor (write_str).
crates/defguard_common/src/db/models/group.rs Minor Display impl refactor (write_str).
crates/defguard_common/src/db/models/device.rs Adds device type pagination helpers + count; minor refactors.
crates/defguard_common/src/db/models/device_login.rs Minor Display impl refactor (write_str).
crates/defguard_common/src/db/models/authentication_key.rs Simplifies fmt imports; minor Display refactor (write_str).
.sqlx/query-fe19743ca54f23d8478c2b5c2e24a0b029fe678f42f85048d76a635eec86b4fd.json sqlx offline metadata for new/updated count query.
.sqlx/query-fbd58e67752002e146073b120ae0e0cfb6870a18b2b0ee2d7ab30b0145f736e3.json sqlx offline metadata for new/updated count query.
.sqlx/query-fbacf1945597ce0155c838d144e15a3bd31e4a90c464ee5fc76e01096672ddb9.json sqlx offline metadata for new paginated query.
.sqlx/query-fb76746f9e3830edad26fab14a9690c61b8b81d92175b34238e72ae8becc3c11.json sqlx offline metadata for new paginated query.
.sqlx/query-f73139bb69e31208db0598d50325f660b0cdafc00e7e729d8fc8357ddf5d9aa7.json sqlx offline metadata for new paginated query.
.sqlx/query-f4c443edb5bcba45fd17a2572c301dfbd5c5ae75e86a41693f84b7c7e610fe29.json sqlx offline metadata for new/updated count query.
.sqlx/query-ee810e6295ffa65a10bf5d2692caa59f15827d148396dba50b117d62719d279c.json sqlx offline metadata for new/updated count query.
.sqlx/query-eb84d653ce75cd9124e26eeb147a8f11aecbf5ef4296d00ba3f4e9b9c64165da.json sqlx offline metadata for new paginated query.
.sqlx/query-ea19fb19954fefe18c5e6b38e8a434abc8e833105cd4ccfbbe9ea2aa4cb5a78b.json sqlx offline metadata for new/updated count query.
.sqlx/query-d6f3acf04ede4e44799cc45a5518787ef696cf528fbd0d41bb4662cf3c28a555.json sqlx offline metadata for new paginated query.
.sqlx/query-c596fb41eda368b05afa2db5d960ed303def29a0ed50b6aac6113385e6bd0845.json sqlx offline metadata for new paginated query.
.sqlx/query-c2bf39775cc4142f18165b1e342731b0d6b7370543b5c5e8cabdfd47487a3056.json sqlx offline metadata for new paginated query.
.sqlx/query-c26d84875185d0edfd825fc0d88043e5032a3b5f0e93f4911ad282df0e4dc909.json sqlx offline metadata for new paginated query.
.sqlx/query-bdb1cfa836d5f288e5409bf95366b7ec6694c76a9b3a2563395aaf97856a4719.json sqlx offline metadata for new paginated query.
.sqlx/query-bb9dc6f26b8eb0fd0217ef9753eadf9f61409818cc9ae3ca31e1d5fbd1236357.json sqlx offline metadata for new/updated count query.
.sqlx/query-bb1a2dd0364e14174e66faca34180d00030cc90495fa21c3745051c4b66017d5.json sqlx offline metadata for new paginated query.
.sqlx/query-adbb2df90cce8e2693f0339b03915b4f14320ee584bea25145b6e35558d41cc3.json sqlx offline metadata for new/updated count query.
.sqlx/query-ad0262ed3631b5bcdb6b872c6875efc89f1a0183676780aa4e3693c219daf516.json sqlx offline metadata for new/updated count query.
.sqlx/query-a7a7d43d4125a983301f3fb7f534c13cebfbadecd3e46b4772e66a8103d0e5be.json sqlx offline metadata for new/updated count query.
.sqlx/query-9ee5fda54016698095d0d300ea795fd3df00c02dc57cd398c85c6da32bf2af8f.json sqlx offline metadata for new paginated query.
.sqlx/query-9acb3537b3f24b8ff0984329139c438c6876553444e2e844e45feaaa7757414d.json sqlx offline metadata for new/updated count query.
.sqlx/query-99bb839e4c27e4cb3b27e48cc15860bb6653c49196296923733d946b865fe711.json sqlx offline metadata for new/updated count query.
.sqlx/query-862f736d8bf5176fbc1389273b76ba752e474117151853e7f8e31c8ab728c0e1.json sqlx offline metadata for new/updated count query.
.sqlx/query-81bd7091de10030f2c9bce6251a26a33e595ba9adc491e2ac33e7bb1b37352e6.json sqlx offline metadata for new/updated count query.
.sqlx/query-804b9751f53ad69e73ffa8adcb49e5aa3193569f98100b2b4021eadf284b14e3.json sqlx offline metadata for new paginated query.
.sqlx/query-7bced9356f619689d57d972bb722f36a5126005e2aa86a4d8a54111ae054b0c5.json sqlx offline metadata for new/updated count query.
.sqlx/query-78aa7073f678fae880f556d20d55976fc8e6356897b9461ff93b5f7a7942852e.json sqlx offline metadata for new/updated count query.
.sqlx/query-779bab5c5182cedc94fde97d8de2cd52eaee9adf329bcb5b8750e667be0fb720.json sqlx offline metadata for new paginated query.
.sqlx/query-723c4cadc9212c4c64171642a6c16bb0e2a54479b73b3e543483407ca70be765.json sqlx offline metadata for new paginated query.
.sqlx/query-70c807776ae7493a9092a2c7e1ff91a2fe6af723ac47149bd48c500308d07c37.json sqlx offline metadata for new paginated query.
.sqlx/query-678a18e6f757c7bd3963363170c41fcdf9d49a068ba62c935661b52d447d1886.json sqlx offline metadata for new/updated count query.
.sqlx/query-6501f1901bd1c0ea058f57f7d6d66d93f52bf976ca867a437323c9d608e1dca9.json sqlx offline metadata for new paginated query.
.sqlx/query-618d9453983f09c9aad6ca6fb15d6558006c515883df414e31030031d8d3ab87.json sqlx offline metadata for new/updated count query.
.sqlx/query-586825cc1bb32392602980b89a02c67f2d6e9bb7ff8a597dfb3227dccf70dd8f.json sqlx offline metadata for new/updated count query.
.sqlx/query-51ee8e7b679a3016d766650b4660cb654e233b6482388e464b43cd12dce91a26.json sqlx offline metadata for new paginated query.
.sqlx/query-50454bc68dc576e6851d69d2f6607ac85ff95cfdd8a2077f1cb3ecb35b50cfb0.json sqlx offline metadata for new paginated query.
.sqlx/query-4ef0f7f062273c6be0de900c481871dc849a9bca1f2b9af9bccb1a660ac560a6.json sqlx offline metadata for new/updated count query.
.sqlx/query-4b5d65bf132381ced1529def2b457c33fb274ca003c5038f20cc41309fe4a887.json sqlx offline metadata for new paginated query.
.sqlx/query-4ac58f741639f60ddd54e71be4186a16173d2a2a7d5643dbadc20495320df0b2.json sqlx offline metadata for new/updated count query.
.sqlx/query-41ed73ad49a5004227995746daac0f8ccd22ce1744cd5173c9ae85080cb47031.json sqlx offline metadata for new paginated query.
.sqlx/query-3f884d7e2275627cccb6edde7dab3fd8d751c34728fa5a1040a4f1c241050f9a.json sqlx offline metadata for new/updated count query.
.sqlx/query-3c9fc8052c5c3f07f27843941dbbc23913f2f0efbc0dee72ae019798529fe2bb.json sqlx offline metadata for new paginated query.
.sqlx/query-3a53fb20d5f1aa1e2cfcb07e1e74a88a2c3cb9ec22a43f01a1bfa8a4307073da.json sqlx offline metadata for new/updated count query.
.sqlx/query-376cfd25e7c3020ab7086de565fbb1feb7195d08aec4183a5bea8fd1565ece83.json sqlx offline metadata for new paginated query.
.sqlx/query-36489e53e85877cb7e58682bdf81b62c85229f8fca873c54f1b190b27cb37fda.json sqlx offline metadata for new/updated count query.
.sqlx/query-2dfb78505182ad04c18910a74973c790ecf981fdd75a9c60944196d01747424d.json sqlx offline metadata for new/updated count query.
.sqlx/query-2d445dd29fbd4a993bdbe4f2a165f4729443afc99fb5a45c75a3b415eda9e877.json sqlx offline metadata for new/updated count query.
.sqlx/query-28df23ae807eec13b0271ceedaafb4e05135d602d2e411353085379b562811b3.json sqlx offline metadata for new/updated count query.
.sqlx/query-266cbdce48f82432dfeafe895d8890d4591eac8851eb8001121badc2c9915e17.json sqlx offline metadata for new paginated query.
.sqlx/query-2343ba38a9b03cb5d630e0065624f7d3d26db7ed1252268326ec832957e0d973.json sqlx offline metadata for new/updated count query.
.sqlx/query-21c42638fa012bb17c4f41cf35a129325d271435cb1c7d97e9a58d064efcd78c.json sqlx offline metadata for new/updated count query.
.sqlx/query-20adf2baa9956dd33289fde61cc2d48c573c0efbf73950059aedd67c5c56440d.json sqlx offline metadata for new paginated query.
.sqlx/query-200a90010f6790cd472074ee3e581516831adc8d7139c7085f2ad2bc380e1d2d.json sqlx offline metadata for new paginated query.
.sqlx/query-1ecd264d768ded66b7a5e84d4750b342c0e69e4ef1f836d824daa7d19d45cbe3.json sqlx offline metadata for new paginated query.
.sqlx/query-1defdac79fce737a99aede47230d952cd0ebfa02ace37f2010d44863c7217318.json sqlx offline metadata for new paginated query.
.sqlx/query-1d54c74b2023ac8cf2a6087ccf6518764abec197f1766049e1915c3c59be0411.json sqlx offline metadata for new paginated query.
.sqlx/query-1ac151a3eda9dd4403b4291eaf9ce575db684cb2a32d6d8b987ddeb7125b7aba.json sqlx offline metadata for new/updated count query.
.sqlx/query-19ad6391ddfb43ab2f1100aefab048e985a9c6ba7c02475756c3e40e9a66b450.json sqlx offline metadata for new paginated query.
.sqlx/query-165afcf1829ca9b94e9f1299c388e10afae39018c4c1035b779585bf591698a4.json sqlx offline metadata for new/updated count query.
.sqlx/query-12457bde75bdc75b7de71fc8af5350395b14466c1d2ba1500ca3662ebec06fce.json sqlx offline metadata for new paginated query.
.sqlx/query-0f0c3641e4c2d677f55b0b1878db9c0b9f25ead9ffcf5e1f534a65ec3c419f81.json sqlx offline metadata updated for ACL rule count query.
.sqlx/query-051c9b90e58622eb0bc07e185ce2e93fee1abd247d1198aaaf286ed4afe54088.json sqlx offline metadata for new paginated query.
Files not reviewed (61)
  • .sqlx/query-051c9b90e58622eb0bc07e185ce2e93fee1abd247d1198aaaf286ed4afe54088.json: Language not supported
  • .sqlx/query-12457bde75bdc75b7de71fc8af5350395b14466c1d2ba1500ca3662ebec06fce.json: Language not supported
  • .sqlx/query-165afcf1829ca9b94e9f1299c388e10afae39018c4c1035b779585bf591698a4.json: Language not supported
  • .sqlx/query-19ad6391ddfb43ab2f1100aefab048e985a9c6ba7c02475756c3e40e9a66b450.json: Language not supported
  • .sqlx/query-1ac151a3eda9dd4403b4291eaf9ce575db684cb2a32d6d8b987ddeb7125b7aba.json: Language not supported
  • .sqlx/query-1d54c74b2023ac8cf2a6087ccf6518764abec197f1766049e1915c3c59be0411.json: Language not supported
  • .sqlx/query-1defdac79fce737a99aede47230d952cd0ebfa02ace37f2010d44863c7217318.json: Language not supported
  • .sqlx/query-1ecd264d768ded66b7a5e84d4750b342c0e69e4ef1f836d824daa7d19d45cbe3.json: Language not supported
  • .sqlx/query-200a90010f6790cd472074ee3e581516831adc8d7139c7085f2ad2bc380e1d2d.json: Language not supported
  • .sqlx/query-20adf2baa9956dd33289fde61cc2d48c573c0efbf73950059aedd67c5c56440d.json: Language not supported
  • .sqlx/query-21c42638fa012bb17c4f41cf35a129325d271435cb1c7d97e9a58d064efcd78c.json: Language not supported
  • .sqlx/query-2343ba38a9b03cb5d630e0065624f7d3d26db7ed1252268326ec832957e0d973.json: Language not supported
  • .sqlx/query-266cbdce48f82432dfeafe895d8890d4591eac8851eb8001121badc2c9915e17.json: Language not supported
  • .sqlx/query-28df23ae807eec13b0271ceedaafb4e05135d602d2e411353085379b562811b3.json: Language not supported
  • .sqlx/query-2d445dd29fbd4a993bdbe4f2a165f4729443afc99fb5a45c75a3b415eda9e877.json: Language not supported
  • .sqlx/query-2dfb78505182ad04c18910a74973c790ecf981fdd75a9c60944196d01747424d.json: Language not supported
  • .sqlx/query-36489e53e85877cb7e58682bdf81b62c85229f8fca873c54f1b190b27cb37fda.json: Language not supported
  • .sqlx/query-376cfd25e7c3020ab7086de565fbb1feb7195d08aec4183a5bea8fd1565ece83.json: Language not supported
  • .sqlx/query-3a53fb20d5f1aa1e2cfcb07e1e74a88a2c3cb9ec22a43f01a1bfa8a4307073da.json: Language not supported
  • .sqlx/query-3c9fc8052c5c3f07f27843941dbbc23913f2f0efbc0dee72ae019798529fe2bb.json: Language not supported
  • .sqlx/query-3f884d7e2275627cccb6edde7dab3fd8d751c34728fa5a1040a4f1c241050f9a.json: Language not supported
  • .sqlx/query-41ed73ad49a5004227995746daac0f8ccd22ce1744cd5173c9ae85080cb47031.json: Language not supported
  • .sqlx/query-4ac58f741639f60ddd54e71be4186a16173d2a2a7d5643dbadc20495320df0b2.json: Language not supported
  • .sqlx/query-4b5d65bf132381ced1529def2b457c33fb274ca003c5038f20cc41309fe4a887.json: Language not supported
  • .sqlx/query-4ef0f7f062273c6be0de900c481871dc849a9bca1f2b9af9bccb1a660ac560a6.json: Language not supported
  • .sqlx/query-50454bc68dc576e6851d69d2f6607ac85ff95cfdd8a2077f1cb3ecb35b50cfb0.json: Language not supported
  • .sqlx/query-51ee8e7b679a3016d766650b4660cb654e233b6482388e464b43cd12dce91a26.json: Language not supported
  • .sqlx/query-586825cc1bb32392602980b89a02c67f2d6e9bb7ff8a597dfb3227dccf70dd8f.json: Language not supported
  • .sqlx/query-618d9453983f09c9aad6ca6fb15d6558006c515883df414e31030031d8d3ab87.json: Language not supported
  • .sqlx/query-6501f1901bd1c0ea058f57f7d6d66d93f52bf976ca867a437323c9d608e1dca9.json: Language not supported
  • .sqlx/query-678a18e6f757c7bd3963363170c41fcdf9d49a068ba62c935661b52d447d1886.json: Language not supported
  • .sqlx/query-70c807776ae7493a9092a2c7e1ff91a2fe6af723ac47149bd48c500308d07c37.json: Language not supported
  • .sqlx/query-723c4cadc9212c4c64171642a6c16bb0e2a54479b73b3e543483407ca70be765.json: Language not supported
  • .sqlx/query-779bab5c5182cedc94fde97d8de2cd52eaee9adf329bcb5b8750e667be0fb720.json: Language not supported
  • .sqlx/query-78aa7073f678fae880f556d20d55976fc8e6356897b9461ff93b5f7a7942852e.json: Language not supported
  • .sqlx/query-7bced9356f619689d57d972bb722f36a5126005e2aa86a4d8a54111ae054b0c5.json: Language not supported
  • .sqlx/query-804b9751f53ad69e73ffa8adcb49e5aa3193569f98100b2b4021eadf284b14e3.json: Language not supported
  • .sqlx/query-81bd7091de10030f2c9bce6251a26a33e595ba9adc491e2ac33e7bb1b37352e6.json: Language not supported
  • .sqlx/query-862f736d8bf5176fbc1389273b76ba752e474117151853e7f8e31c8ab728c0e1.json: Language not supported
  • .sqlx/query-99bb839e4c27e4cb3b27e48cc15860bb6653c49196296923733d946b865fe711.json: Language not supported
  • .sqlx/query-9acb3537b3f24b8ff0984329139c438c6876553444e2e844e45feaaa7757414d.json: Language not supported
  • .sqlx/query-9ee5fda54016698095d0d300ea795fd3df00c02dc57cd398c85c6da32bf2af8f.json: Language not supported
  • .sqlx/query-a7a7d43d4125a983301f3fb7f534c13cebfbadecd3e46b4772e66a8103d0e5be.json: Language not supported
  • .sqlx/query-ad0262ed3631b5bcdb6b872c6875efc89f1a0183676780aa4e3693c219daf516.json: Language not supported
  • .sqlx/query-adbb2df90cce8e2693f0339b03915b4f14320ee584bea25145b6e35558d41cc3.json: Language not supported
  • .sqlx/query-bb1a2dd0364e14174e66faca34180d00030cc90495fa21c3745051c4b66017d5.json: Language not supported
  • .sqlx/query-bb9dc6f26b8eb0fd0217ef9753eadf9f61409818cc9ae3ca31e1d5fbd1236357.json: Language not supported
  • .sqlx/query-bdb1cfa836d5f288e5409bf95366b7ec6694c76a9b3a2563395aaf97856a4719.json: Language not supported
  • .sqlx/query-c26d84875185d0edfd825fc0d88043e5032a3b5f0e93f4911ad282df0e4dc909.json: Language not supported
  • .sqlx/query-c2bf39775cc4142f18165b1e342731b0d6b7370543b5c5e8cabdfd47487a3056.json: Language not supported
  • .sqlx/query-c596fb41eda368b05afa2db5d960ed303def29a0ed50b6aac6113385e6bd0845.json: Language not supported
  • .sqlx/query-d6f3acf04ede4e44799cc45a5518787ef696cf528fbd0d41bb4662cf3c28a555.json: Language not supported
  • .sqlx/query-ea19fb19954fefe18c5e6b38e8a434abc8e833105cd4ccfbbe9ea2aa4cb5a78b.json: Language not supported
  • .sqlx/query-eb84d653ce75cd9124e26eeb147a8f11aecbf5ef4296d00ba3f4e9b9c64165da.json: Language not supported
  • .sqlx/query-ee810e6295ffa65a10bf5d2692caa59f15827d148396dba50b117d62719d279c.json: Language not supported
  • .sqlx/query-f4c443edb5bcba45fd17a2572c301dfbd5c5ae75e86a41693f84b7c7e610fe29.json: Language not supported
  • .sqlx/query-f73139bb69e31208db0598d50325f660b0cdafc00e7e729d8fc8357ddf5d9aa7.json: Language not supported
  • .sqlx/query-fb76746f9e3830edad26fab14a9690c61b8b81d92175b34238e72ae8becc3c11.json: Language not supported
  • .sqlx/query-fbacf1945597ce0155c838d144e15a3bd31e4a90c464ee5fc76e01096672ddb9.json: Language not supported
  • .sqlx/query-fbd58e67752002e146073b120ae0e0cfb6870a18b2b0ee2d7ab30b0145f736e3.json: Language not supported
  • .sqlx/query-fe19743ca54f23d8478c2b5c2e24a0b029fe678f42f85048d76a635eec86b4fd.json: Language not supported
Comments suppressed due to low confidence (1)

crates/defguard_core/src/handlers/pagination.rs:88

  • PaginationMeta::from_pagination treats per_page <= 1 as a special case but still reports page_size: per_page (so 0 is possible) and computes total_pages = total_items, which yields odd metadata for empty collections (e.g., current_page=1, total_pages=0) and can produce next_page even when LIMIT 0 returns no data. It would be more consistent to normalize per_page to at least 1 and compute total_pages with div_ceil (optionally with a minimum of 1 page when total_items == 0).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants