api-pagination/SKILL.md
Offset pagination pattern for PHP REST APIs and mobile clients (Android Jetpack Compose + iOS SwiftUI). Covers backend response format, client DTOs, repository, ViewModel state, and infinite-scroll UI. Use when adding pagination to any list endpoint.
npx skillsauth add peterbamuhigire/skills-web-dev api-paginationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
api-pagination or would be better handled by a more specific companion skill.SKILL.md first, then load only the referenced deep-dive files that are necessary for the task.Standard offset-based pagination pattern used across the Maduuka platform. Applies to the PHP backend (REST API) and mobile clients (Android Kotlin + Compose, iOS SwiftUI).
Pattern: Backend returns data.items[] + data.pagination{}. Mobile clients append items on scroll, track page/totalPages in ViewModel state.
Deployment: Backend runs on Windows dev (MySQL 8.4.7), Ubuntu staging (MySQL 8.x), Debian production (MySQL 8.x). Pagination queries must use utf8mb4_unicode_ci collation and work identically on all platforms.
Every paginated list endpoint MUST return this structure:
{
"success": true,
"data": {
"items": [ ... ],
"pagination": {
"page": 1,
"per_page": 30,
"total": 142,
"total_pages": 5
}
}
}
<?php
declare(strict_types=1);
require_once __DIR__ . '/../middleware.php';
require_method('GET');
$auth = require_auth();
$db = get_db();
$franchiseId = (int)$auth['franchise_id'];
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = min(100, max(1, (int)($_GET['per_page'] ?? 30)));
// Optional filters
$status = isset($_GET['status']) ? trim((string)$_GET['status']) : '';
try {
$where = 't.franchise_id = :fid';
$params = ['fid' => $franchiseId];
if ($status !== '') {
$where .= ' AND t.status = :status';
$params['status'] = $status;
}
// 1. Count total
$countSql = "SELECT COUNT(*) FROM tbl_example t WHERE {$where}";
$countStmt = $db->prepare($countSql);
$countStmt->execute($params);
$total = (int)$countStmt->fetchColumn();
$countStmt->closeCursor(); // IMPORTANT for PDO
$totalPages = $total > 0 ? (int)ceil($total / $perPage) : 1;
$offset = ($page - 1) * $perPage;
// 2. Fetch page
$sql = "SELECT t.* FROM tbl_example t WHERE {$where}
ORDER BY t.created_at DESC
LIMIT :lim OFFSET :off";
$queryParams = $params;
$queryParams['lim'] = $perPage;
$queryParams['off'] = $offset;
$stmt = $db->prepare($sql);
foreach ($queryParams as $key => $value) {
if ($key === 'lim' || $key === 'off') {
$stmt->bindValue($key, $value, PDO::PARAM_INT);
} else {
$stmt->bindValue($key, $value);
}
}
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 3. Return with pagination metadata
json_response(200, [
'success' => true,
'data' => [
'items' => $rows,
'pagination' => [
'page' => $page,
'per_page' => $perPage,
'total' => $total,
'total_pages' => $totalPages,
],
],
]);
} catch (Throwable $e) {
json_response(500, [
'success' => false,
'message' => 'Failed to load items',
'error' => $e->getMessage(),
]);
}
closeCursor() after the COUNT query before running the main query (PDO requirement).PDO::PARAM_INT — string binding causes MySQL errors.per_page at 100 to prevent abuse: min(100, max(1, ...)).per_page is 30 for list screens, 50 for stock-level screens.data.items + data.pagination — NEVER return a flat array in data.Extended guidance for api-pagination was moved to references/skill-deep-dive.md to keep this entrypoint compact and fast to load.
Use that deep dive for:
Android Client PatternKey Compose Imports for PaginationiOS Client PatternImportant RulesExisting Implementationsdata-ai
Use when adding AI-powered analytics to a SaaS platform — semantic search over business data, natural language queries, trend detection, anomaly alerts, and AI-generated insights for dashboards. Covers embeddings, NL2SQL, and per-tenant analytics...
data-ai
Design AI-powered analytics dashboards — what metrics to show, how to display AI predictions and confidence, drill-down patterns, KPI cards, trend visualisation, AI Insights panels, export design, and role-based dashboard variants. Invoke when...
development
Use when designing, building, reviewing, or upgrading production software systems that must be secure, performant, maintainable, scalable, and user-centered. Apply before writing specs, code, architecture, APIs, databases, mobile apps, SaaS platforms, or ERP systems.
development
Professional web app UI using commercial templates (Tabler/Bootstrap 5) with strong frontend design direction when needed. Use for CRUD interfaces, dashboards, admin panels with SweetAlert2, DataTables, Flatpickr. Clone seeder-page.php, use...