Follow
A follow edge points from one feed to another, e.g. timeline:maya follows user:diego.
There is no backfill: a follow only affects activities posted after the edge exists
(copy_limit=0, spec §1) — following someone doesn’t retroactively pull their history
into your timeline. Follower/following counts are denormalized (spec §4) and read without
a COUNT(*).
Follow
Section titled “Follow”// client.feed(sourceGroup, sourceId).follow(targetGroup, targetId)await client.feed('timeline', 'maya').follow('user', 'diego')// Same shape, server token — no user token to mint.await server.feed('timeline', 'maya').follow('user', 'diego')
// Sugar for the 95% case, "maya follows diego":await server.userFollow({ follower: 'maya', following: 'diego' })Use this to mirror follows your own system records — a database trigger, a webhook,
an admin action. Diego is notified and follow.added fires, exactly as if maya had
followed from the client.
Do not reach for batch.userFollows for live follows: batch is the
quiet import path and notifies nobody. Its job is
the one-off import described below —
which you want to run before the first mirrored follow, not instead of it.
Re-following an existing edge writes nothing and notifies nobody, so an at-least-once trigger can safely deliver the same follow twice.
import { useFollow } from '@dropinnodex/react'
function FollowButton({ targetId }: { targetId: string }) { const { follow, unfollow, isFollowing } = useFollow('timeline', 'maya') const following = isFollowing('user', targetId) return ( <button onClick={() => (following ? unfollow('user', targetId) : follow('user', targetId))}> {following ? 'Following' : 'Follow'} </button> )}follow/unfollow update isFollowing() optimistically and roll back on error.
Unfollow
Section titled “Unfollow”await client.feed('timeline', 'maya').unfollow('user', 'diego')await server.feed('timeline', 'maya').unfollow('user', 'diego')await server.userUnfollow({ follower: 'maya', following: 'diego' })const { unfollow } = useFollow('timeline', 'maya')await unfollow('user', 'diego')Unfollowing hard-deletes the edge and enqueues a scrub of the follower’s copies of
that feed’s activities, so their timeline loses the history shortly after — the delete is
immediate, the scrub is a background job. Re-following is a genuine
new edge — new created_at, a fresh follow.added webhook — but it does not produce
a second notification: those are unique per (owner, verb, actor, object) for the life of
the account, so unfollow/re-follow cannot be used to ping someone repeatedly. See
what will never create a second notification.
And re-following does not backfill: like any follow, the timeline picks up the target’s activities from the next post onward, not the ones posted while unfollowed.
Import the existing graph before you mirror it
Section titled “Import the existing graph before you mirror it”Mirroring live follows only covers edges created from now on. If your product already has a follow graph, every existing user’s timeline is empty on day one — they follow people, and nothing arrives, because dropin has never heard of those edges.
So the order is: import first, then start mirroring.
// One-off, before (or alongside) the trigger that mirrors live follows.await server.batch.userFollows( edges.map(({ follower, following }) => ({ follower, following })),)100 edges per call, quiet — nobody is notified that they were “followed” by someone who followed them two years ago. Full recipe, including how to page a large graph: Migrating existing data.
Following yourself
Section titled “Following yourself”Self-follow is accepted: maya may follow maya, and it increments her public
follower_count like any other edge. Nothing rejects it, so do not use it as a trick
to get someone’s own posts into their timeline — the inflated counter is visible to
every reader.
A timeline contains exactly what its follow edges bring in, and nothing else — a
user’s own posts land in user:maya, not timeline:maya. To render “my feed
including my own posts”, read both and merge client-side:
const [mine, timeline] = await Promise.all([ client.feed('user', 'maya').get(), client.feed('timeline', 'maya').get(),])Merge on time descending, and de-duplicate on id — an activity can legitimately
appear in both if someone you follow reposted it into a feed you also read.
Read follower/following counts
Section titled “Read follower/following counts”const stats = await client.feed('user', 'diego').followStats()stats.follower_countstats.following_countimport { useFollowStats } from '@dropinnodex/react'
const { followerCount, followingCount, isLoading } = useFollowStats('user', 'diego')const stats = await server.feed('user', 'diego').followStats()Read the follower/following lists
Section titled “Read the follower/following lists”For the lists themselves rather than the counts. Both return a Page<Follow>, newest
edge first, keyset-paginated.
const followers = await client.feed('user', 'diego').followers({ limit: 20 })const following = await client.feed('timeline', 'maya').following({ limit: 20 })const followers = await server.feed('user', 'diego').followers({ limit: 20 })const following = await server.feed('timeline', 'maya').following({ limit: 20 })import { useFollowers, useFollowing } from '@dropinnodex/react'
// The hooks hydrate the first page only — { group, id } pairs, plus refresh().const { followers, isLoading, refresh } = useFollowers('user', 'diego')const { following } = useFollowing('timeline', 'maya')Use the client SDK directly when you need to page beyond the first response.
Suggest who to follow
Section titled “Suggest who to follow”Suggestions are user: feeds a feed doesn’t already follow, ranked by mutual overlap
(feeds followed by feeds it follows — friends-of-friends), then topped up by global
popularity for a cold-start feed with a thin graph. Call it on the timeline:<id>
feed — following happens from the timeline side (spec §5), so that’s the graph walked.
const { results } = await client.feed('timeline', 'diego').suggestions({ limit: 25 })results // [{ group: 'user', id: 'anna', mutuals: 3 }, …] — best firstimport { useSuggestions } from '@dropinnodex/react'
const { suggestions, isLoading, refresh } = useSuggestions('timeline', 'diego', { limit: 5 })// after the viewer follows one, call refresh() to drop it from the listconst { results } = await server.feed('timeline', 'diego').suggestions({ limit: 25 })mutuals is how many of the caller’s follows also follow that suggestion; a popularity-fill
suggestion has mutuals: 0. This is a capped top-N, not a page — limit defaults to 25
(max 50), and there’s no cursor or next. Reads are open within a tenant, so any valid
token — including a server-minted one — can fetch suggestions for any feed.
Related
Section titled “Related”- Feeds — the timeline these edges feed, and why a new follow shows no history.
- Notifications — what the followed user receives.
- Migrating existing data — importing an existing follow graph, quietly, versus mirroring live follows.
- Webhooks —
follow.added/follow.removed. - Promoted activities — targeting an audience by following users into
a feed such as
city:belgrade.