From 147d85abf668fa1413fd4684b1d3a35cfdd50b77 Mon Sep 17 00:00:00 2001 From: posimai Date: Fri, 17 Apr 2026 18:34:06 +0900 Subject: [PATCH] =?UTF-8?q?feat(together):=20cursor-based=20pagination=20A?= =?UTF-8?q?PI=20=E2=80=94=20limit/cursor=20=E3=82=AF=E3=82=A8=E3=83=AA?= =?UTF-8?q?=E3=83=91=E3=83=A9=E3=83=A1=E3=83=BC=E3=82=BF=E3=83=BC=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/server.js b/server.js index 6fdcdf38..73c4cb8d 100644 --- a/server.js +++ b/server.js @@ -2547,8 +2547,17 @@ ${excerpt} }); // GET /together/feed/:groupId — フィード(リアクション・コメント数付き) + // ?limit=N&cursor= でカーソルページネーション対応 r.get('/together/feed/:groupId', async (req, res) => { try { + const limit = Math.min(parseInt(req.query.limit) || 20, 50); + const cursor = req.query.cursor; // shared_at の ISO タイムスタンプ + const params = [req.params.groupId]; + let cursorClause = ''; + if (cursor) { + params.push(cursor); + cursorClause = 'AND s.shared_at < $2'; + } const result = await pool.query(` SELECT s.*, @@ -2560,12 +2569,16 @@ ${excerpt} FROM together_shares s LEFT JOIN together_reactions r ON r.share_id = s.id LEFT JOIN together_comments c ON c.share_id = s.id - WHERE s.group_id = $1 + WHERE s.group_id = $1 ${cursorClause} GROUP BY s.id ORDER BY s.shared_at DESC - LIMIT 50 - `, [req.params.groupId]); - res.json(result.rows); + LIMIT ${limit + 1} + `, params); + const rows = result.rows; + const hasMore = rows.length > limit; + const items = hasMore ? rows.slice(0, limit) : rows; + const nextCursor = items.length > 0 ? items[items.length - 1].shared_at : null; + res.json({ items, next_cursor: nextCursor, has_more: hasMore }); } catch (e) { console.error('[together/feed]', e.message); res.status(500).json({ error: 'Internal server error' });