Skip to content

♻️ Add Universal Utility to Paginate Any Array Data (No DB Needed) #23

@abhishek-nexgen-dev

Description

@abhishek-nexgen-dev

📚 Overview

Build a highly reusable, type-safe Pagination Utility Class that paginates any array (in-memory data). This is a common need across many backend applications — especially when you're dealing with data that's already loaded or not coming directly from a database query.


💡 Why This Is Useful

🔍 Use Case Example

🧠 In-memory filtering A user searches/filter locally loaded data (not DB query)
📋 Static dropdowns or tags Tags, categories, or form options
🔄 Enum lists Role types, statuses, languages
⚡️ Cached data Data pulled once, paginated multiple times
📦 Responses without ORM Data from an API call, file, or mock

This class ensures that developers don’t have to write repetitive .slice() logic in multiple places. It also provides consistent metadata output (like total pages, current page, items per page) for UI and frontend devs.


📁 File Location

src/utils/PaginateArray.ts


🎯 Goal

Create a single reusable utility class called PaginateArray that:

✅ Accepts any array
✅ Accepts page, limit, and maxLimit options
✅ Calculates total count, total pages, and perPage
✅ Returns paginated result + metadata
✅ Works in any controller, service, or standalone script


🧠 Implementation Details

✅ Interface Definitions:

export interface PaginationOptions {
page?: number;
limit?: number;
maxLimit?: number;
}

export interface PaginationMeta {
totalItems: number;
totalPages: number;
currentPage: number;
perPage: number;
}

export interface PaginatedArrayResult {
data: T[];
meta: PaginationMeta;
}

✅ Main Class:

export class PaginateArray {
static paginate(inputArray: T[], options: PaginationOptions = {}): PaginatedArrayResult {
const page = Math.max(1, options.page || 1);
const limit = Math.min(options.limit || 10, options.maxLimit || 100);
const skip = (page - 1) * limit;

const paginatedData = inputArray.slice(skip, skip + limit);
const totalItems = inputArray.length;
const totalPages = Math.ceil(totalItems / limit);

return {
  data: paginatedData,
  meta: {
    totalItems,
    totalPages,
    currentPage: page,
    perPage: limit,
  },
};

}
}


💻 Example Usage

🧾 1. In Controller (Express)

import { PaginateArray } from '@/utils/PaginateArray';

const allTags = ['Node.js', 'React', 'Vue', 'Express', 'MongoDB'];

const result = PaginateArray.paginate(allTags, {
page: Number(req.query.page),
limit: Number(req.query.limit),
});

SendResponse.success(res, result, 'Tags fetched successfully');


🧾 2. In a Service

export const getPaginatedCachedLogs = (logs: LogType[], query: any) => {
return PaginateArray.paginate(logs, {
page: Number(query.page),
limit: Number(query.limit),
});
};


✅ Output Format (for frontend)

{
"data": ["React", "Vue"],
"meta": {
"totalItems": 5,
"totalPages": 3,
"currentPage": 2,
"perPage": 2
}
}


📦 Expected Benefits

🧩 Easily used in dropdowns, filtered lists, enums, or reports

♻️ Clean code — no more writing .slice() in every controller

✅ Fully type-safe

🚀 Speeds up feature development


🔧 Optional Enhancements (Future PRs)

[ ] Add sortBy, filterBy parameters

[ ] Add ability to return just meta (for frontend placeholder)

[ ] Add integration with SendResponse.success() as default return


🏷️ Suggested Labels

feature, utils, pagination, core, good first issue, reusable


🙋 Contribution Tips

If you're picking this up:

Keep it framework-agnostic (no Express or Mongoose here)

Use TypeScript generics for flexibility

Return clean and consistent metadata

Write 1-2 usage examples in a demo folder (PaginateArray.demo.ts)


Let me know if you'd like a second version of this that works for database queries or for cursor-based pagination.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions