accesspatterns.dev
← All models

social

Notifications Feed

A per-user feed read a page at a time, with unread as a sparse marker.

A feed is an item collection that never stops growing, so it is never read whole. This model is about the cursor: a page of notifications, a key to resume from, and an unread marker that only unread items carry.

The model

Notification

One notification, newest sorting last.

pk
USER#<userId>
sk
NOTIF#<createdOn>#<notificationId>

Attributes: kind (S), actor (S), createdAt (N), unread (S), GSI1PK (S), GSI1SK (S)

Access patterns

  • QueryRead a page of the feed

    One user's notifications, newest first, a page at a time.

  • Query · GSI1Count what is unread

    Only the unread notifications, from an index that holds nothing else.

Design notes

The cursor is a key, not an offsetwhy

A page comes back with a LastEvaluatedKey, and the next request passes it as ExclusiveStartKey. There is no page number and no OFFSET: the read resumes at an exact position in the collection, so page fifty costs what page one costs. A relational offset walks and discards everything before it, which is why deep pagination gets slower there and does not here.

Unread is absent, not falsewhy

Read notifications carry neither the unread attribute nor the index keys. That is what makes GSI1 sparse: an item with no GSI1PK is simply not in the index, so the index holds only unread notifications and the badge count is a read of something small rather than a walk of the whole feed. Marking one read means removing its index keys, and it leaves the index on its own - no delete, no sweep.

The page size is the request's businesstrade-off

Limit belongs to the read, not to the table, so the same collection serves a panel showing five and a full page showing fifty. The preview console reads the collection without a Limit, so it returns every notification rather than the first page of one.

Taught in the course

notifications-feed
Try an example

One user's notifications, newest first, a page at a time.

PK(pk)
SK(sk)
GSI1PK
GSI1SK
actor
createdAt
kind
unread
USER#u-ada
NOTIF#2026-07-30T00:00:00Z#n-8041S
samS
1785369600N
commentS
USER#u-ada
NOTIF#2026-08-05T00:00:00Z#n-8320S
rajS
1785888000N
assignedS
USER#u-ada
NOTIF#2026-08-10T00:00:00Z#n-8604S
meiS
1786320000N
mentionS
USER#u-ada
NOTIF#2026-08-12T00:00:00Z#n-8790S
UNREAD#u-adaS
2026-08-12T00:00:00Z#n-8790S
samS
1786492800N
commentS
yS
USER#u-ada
NOTIF#2026-08-13T00:00:00Z#n-8871S
UNREAD#u-adaS
2026-08-13T00:00:00Z#n-8871S
meiS
1786579200N
review_requestedS
yS
USER#u-ada
NOTIF#2026-08-13T19:12:00Z#n-9012S
UNREAD#u-adaS
2026-08-13T19:12:00Z#n-9012S
rajS
1786648320N
mentionS
yS
USER#u-raj
NOTIF#2026-08-07T00:00:00Z#n-8402S
meiS
1786060800N
mentionS
USER#u-raj
NOTIF#2026-08-13T12:00:00Z#n-8998S
UNREAD#u-rajS
2026-08-13T12:00:00Z#n-8998S
adaS
1786622400N
commentS
yS

Run an operation to see the raw engine response.

table notifications-feedkeys PK / SKitems 8

The in-browser engine is a preview build. Transactions, vector search, streams, tags and TTL are among the operations it doesn't implement yet.