Skip to content

Feat/pagination chat list#200

Open
icancodefyi wants to merge 2 commits into
avishek0769:mainfrom
icancodefyi:feat/pagination-chat-list
Open

Feat/pagination chat list#200
icancodefyi wants to merge 2 commits into
avishek0769:mainfrom
icancodefyi:feat/pagination-chat-list

Conversation

@icancodefyi

Copy link
Copy Markdown
Contributor

Closes #46

Summary
Paginate the chat list endpoint so heavy users don't load everything in one request. Uses cursor-based pagination for stable ordering even when new chats are created mid-pagination.
Changes
Backend — backend/controllers/chat.controller.js

  • Replaced monolithic listAllChats with cursor-based pagination using (createdAt, id) compound cursor
  • Accepts ?limit=25&cursor= query params (limit: 1–100, default 25)
  • Returns { chats, hasMore, nextCursor } for infinite scroll / "Load More" UX
  • Replaced per-chat usageEvents eager loading with a single groupBy aggregation — eliminates N+1 and reduces payload size
    Backend — backend/utils/validationSchemas.js
  • Added listChatsQuerySchema with zod validation for limit (clamped 1–100) and cursor (base64 string)
    Backend — backend/routers/chat.route.js
  • Added validate(listChatsQuerySchema) middleware to GET /chat/list
    Frontend — src/lib/api.ts
  • getChats() now accepts { limit?, cursor? } params
  • Returns PaginatedChats type ({ chats, hasMore, nextCursor })
  • Removed caching (pagination-aware caching deferred; first page loads fresh)
    Frontend — src/pages/AllChats.tsx
  • Initial load fetches first 25 chats
  • "Load More" button appends older chats using nextCursor
  • Search/filter still works client-side on the accumulated result set

- Backend: cursor pagination with (createdAt, id) compound cursor,
  usage aggregation via groupBy instead of loading all usageEvents
- Validation: listChatsQuerySchema for limit (1-100, default 25) and cursor
- Frontend: getChats accepts pagination params, returns PaginatedChats
- AllChats: Load More button appends older chats, search/filter on loaded set
Copilot AI review requested due to automatic review settings June 11, 2026 18:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR centralizes Markdown code rendering into a reusable CodeBlock component and adds cursor-based pagination to the chats listing, updating both frontend and backend to support incremental loading.

Changes:

  • Extracted duplicated code highlighting/copy UI into src/components/CodeBlock.tsx and reused it across chat pages.
  • Implemented cursor-based pagination for /chat/list in the backend and updated the frontend AllChats page to support “Load More”.
  • Updated the client API contract for getChats to return a paginated envelope (chats/hasMore/nextCursor).

Reviewed changes

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

Show a summary per file
File Description
src/pages/SharedChatPage.tsx Replaces inline code highlighting UI with shared CodeBlock.
src/pages/ChatPage.tsx Replaces inline code highlighting UI with shared CodeBlock.
src/pages/AllChats.tsx Adds “Load More” pagination flow and state management.
src/lib/api.ts Updates getChats to accept pagination params and return a paginated envelope.
src/components/CodeBlock.tsx Introduces reusable highlighted code block with copy-to-clipboard UX.
backend/utils/validationSchemas.js Adds query validation for chat listing pagination params.
backend/routers/chat.route.js Applies new query validation to /chat/list.
backend/controllers/chat.controller.js Implements cursor pagination + usage aggregation changes for chat listing.

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

Comment on lines +271 to +282
const { limit, cursor } = req.query;

const where = { userId: req.user.id };

if (cursor) {
const decoded = JSON.parse(Buffer.from(cursor, "base64").toString());
const cursorDate = new Date(decoded.createdAt);
where.OR = [
{ createdAt: { lt: cursorDate } },
{ createdAt: cursorDate, id: { lt: decoded.id } },
];
}
});

const listAllChats = asyncHandler(async (req, res) => {
const { limit, cursor } = req.query;
Comment on lines +295 to +296
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
take: limit + 1,
Comment thread src/lib/api.ts
Comment on lines +201 to +208
export const getChats = (params?: { limit?: number; cursor?: string }) => {
const query = new URLSearchParams();
if (params?.limit) query.set("limit", String(params.limit));
if (params?.cursor) query.set("cursor", params.cursor);
const qs = query.toString();
const path = `/chat/list${qs ? `?${qs}` : ""}`;
return apiRequest<PaginatedChats>(path, { method: "GET" });
};
Comment on lines +40 to +49
const highlighted = useMemo(() => {
try {
if (language && hljs.getLanguage(language)) {
return hljs.highlight(code, { language }).value;
}
return hljs.highlightAuto(code).value;
} catch {
return escapeHtml(code);
}
}, [language, code]);
@avishek0769 avishek0769 added Hard This is issue is hard to solve SSoC26 Social Summer of Code - 2026 labels Jun 12, 2026
@avishek0769

Copy link
Copy Markdown
Owner

@icancodefyi Please resolve the merge conflicts

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

Labels

Hard This is issue is hard to solve SSoC26 Social Summer of Code - 2026

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add pagination to GET /chat/list and update AllChats to use it

3 participants