- Published at
Simple Infinite Feed Pagination using Postgres

How to enable inifinite scrolling with backend-backed session management
- Authors
-
-
- Name
- Joachim Bülow
- Cofounder and CTO at Doubble
-
Table of Contents
Here is how i structure infinite feed pagination with super simple implementation.
The key is to let clients maintain a session_id
throughout their scroll session, and store the items you look at in a special table.
Database Setup
First, we need to set up a database table to manage feed sessions. This table will store session IDs and item IDs, allowing us to track which items have been seen in a session.
CREATE TABLE feed_session
(
session_id UUID NOT NULL DEFAULT uuid_generate_v4(),
item_id UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (session_id, item_id)
) WITH (ttl_expire_after = '1 day', ttl_job_cron = '@daily');
This SQL snippet creates a feed_session
table with a time-to-live (TTL) of one day, ensuring that session data is automatically cleaned up daily.
Leveraging feed sessions to get unique items
In my example here, I will use TypeORM
to fetch some data. The concept is simple.
- Start a transaction
- Lock the
feed_session
row to prevent concurrent modifications - Fetch the data
- Save the data to the
feed_session
table - Return the data
const result = await this.dataSource.transaction(async (manager) => {
await manager.query(
`
SELECT * FROM feed_session
WHERE session_id = $1
FOR UPDATE
`,
[sessionId],
);
const queryRes = await manager.query(
`
WITH seen_items AS (
SELECT item_id
FROM feed_session
WHERE session_id = $1
),
-- add your query here
SELECT id
FROM foo
LEFT JOIN seen_items ON foo.id = seen_items.item_id
WHERE seen_items.item_id IS NULL -- filter out seen items
`,
[
sessionId, // use this to filter
],
);
// Save session items to avoid seeing same groups in different sessions
const feedSessions = queryRes.map(
(row) =>
new FeedSession({
sessionId,
itemId: row.id,
}),
);
await manager.save(feedSessions);
return result;
});
Conclusion
Easy peasy.
Only downside is your functions will become a bit more bloaty.
But usually an application only have so many infinite feeds.