diff --git a/backend/package.json b/backend/package.json index 8451ab8b..6f55da40 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc", - "start": "node dist/index.js" + "start": "node dist/index.js", + "test": "vitest run" }, "dependencies": { "@anthropic-ai/sdk": "^0.90.0", diff --git a/backend/src/lib/__tests__/chatPagination.test.ts b/backend/src/lib/__tests__/chatPagination.test.ts new file mode 100644 index 00000000..bdfef919 --- /dev/null +++ b/backend/src/lib/__tests__/chatPagination.test.ts @@ -0,0 +1,18 @@ +import { readFileSync } from "fs"; +import { join } from "path"; +import { describe, it, expect } from "vitest"; + +describe("GET /chat pagination", () => { + const src = readFileSync( + join(__dirname, "../../routes/chat.ts"), + "utf8", + ); + + it("applies a limit to the chat list query", () => { + expect(src).toMatch(/\.limit\(/); + }); + + it("supports a before-cursor for pagination", () => { + expect(src).toMatch(/before|lt\("created_at"/); + }); +}); diff --git a/backend/src/routes/chat.ts b/backend/src/routes/chat.ts index fe272c67..34c850d5 100644 --- a/backend/src/routes/chat.ts +++ b/backend/src/routes/chat.ts @@ -142,6 +142,17 @@ chatRouter.get("/", requireAuth, async (req, res) => { const userId = res.locals.userId as string; const db = createServerSupabase(); + // Parse pagination params + const rawLimit = Number(req.query.limit); + const limit = Number.isFinite(rawLimit) && rawLimit > 0 + ? Math.min(rawLimit, 200) + : 50; + const rawBefore = typeof req.query.before === "string" ? req.query.before : null; + if (rawBefore !== null && Number.isNaN(new Date(rawBefore).getTime())) { + return void res.status(400).json({ detail: "before must be a valid ISO 8601 timestamp" }); + } + const before = rawBefore; + const { data: ownProjects, error: projErr } = await db .from("projects") .select("id") @@ -156,11 +167,18 @@ chatRouter.get("/", requireAuth, async (req, res) => { ? `user_id.eq.${userId},project_id.in.(${ownProjectIds.join(",")})` : `user_id.eq.${userId}`; - const { data, error } = await db + let query = db .from("chats") .select("*") .or(filter) - .order("created_at", { ascending: false }); + .order("created_at", { ascending: false }) + .limit(limit); + + if (before) { + query = query.lt("created_at", before); + } + + const { data, error } = await query; if (error) return void res.status(500).json({ detail: error.message }); res.json(data ?? []); }); diff --git a/backend/tsconfig.json b/backend/tsconfig.json index a4b3abf6..80885d66 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -16,5 +16,5 @@ } }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist", "src/**/__tests__"] }