Friend List and Status Feed
A friend list shows which of a user's contacts are currently online. A status feed shows updates those contacts post. Both are views onto the same underlying shape: a follow/follower graph where each person's activity needs to reach everyone following them. Neither view works if every follower has to subscribe to every person they follow, one channel at a time. This page covers a pattern built from two fixed channels per user and the channel groups that Architectural choices introduces as a fan-out mechanism for social features like this one.
This is a follow/follower model, not a general graph. It answers "which of my friends are online" and "what did my friends post," not multi-degree questions like mutual friends or shortest path between two users.
Give every user two fixed channels
Each user gets two channels that never change once created:
- A status channel, such as
user-a.status, that the user publishes updates to. - An online channel, such as
user-a.online, that the user subscribes to for no reason other than to be present on it. Nothing is ever published there. Its only job is to give other users something to track presence on.
Keeping these fixed and per-user is what makes the rest of the pattern work. Your server can reference "user A's status channel" forever, without knowing in advance who will end up following user A.
Fan many friends into one subscription with channel groups
A channel group is a server-managed, named list of channels that a client subscribes to as one unit. Adding or removing a channel from the group changes what every subscribed client receives, with no client resubscribing. That property is what lets a friend list grow or shrink without the viewing client ever touching its own subscription.
Give each user two channel groups, built from the fixed channels of everyone they follow:
cg-user-a-online, holding the online channel of every person user A follows.cg-user-a-feed, holding the status channel of every person user A follows.
User A subscribes to both groups exactly once. From then on, following or unfollowing someone is a change your server makes to group membership, not a change user A's client makes to its own subscription list.
Your server adds each followed person's online channel to cg-user-a-online and their status channel to cg-user-a-feed. User A's client subscribes to cg-user-a-online with receivePresenceEvents to receive presence events, and to cg-user-a-feed to receive status messages. Both subscriptions also cover every friend added to the groups later.
Channel groups require the Stream Controller add-on enabled on the keyset, and both the number of groups and the channels per group carry limits that scale with your plan. Refer to Core concepts and Plan comparison before committing to two groups per user at a scale where that matters, and contact PubNub Support if your design needs a higher ceiling.
See who's online without subscribing to their messages
Subscribing to cg-user-a-online for ordinary messages would be pointless, since nothing is ever published on an online channel. What you actually want is Presence join, leave, and timeout events for every channel currently in that group. Current SDKs give you that through the receivePresenceEvents option on the group's subscription, the same option a single channel's subscription uses:
const onlineGroup = pubnub.channelGroup('cg-user-a-online');
const onlineSubscription = onlineGroup.subscription({ receivePresenceEvents: true });
onlineSubscription.onPresence = (event) => {
console.log(`${event.uuid} is now ${event.action}`); // join, leave, or timeout
};
onlineSubscription.subscribe();
const feedGroup = pubnub.channelGroup('cg-user-a-feed');
const feedSubscription = feedGroup.subscription();
feedSubscription.onMessage = (event) => {
console.log('Status update:', event.message);
};
show all 17 linesThe snippet above is illustrative rather than a complete program. Call Here Now once against the same group to seed the initial online list, then rely on presence events to keep it current. For the full presence event model, including the -pnpres mechanism this option builds on, refer to Presence.
Expand the graph from your server, not the client
Following someone means your server adds their online and status channels to the follower's two groups; unfollowing removes them. Do this from your server, never from the client. A channel group's manage permission lets whoever holds it add or remove any channel in that group. A client that could manage its own groups could just as easily add itself to someone else's feed.
Grant manage only to server-side tokens, and grant clients read on their own groups so they can subscribe. Refer to Access Manager for the permission model, and to Security best practices for why token issuance belongs on your server in general.
To show a name or avatar next to each entry in the online list, store that profile data in App Context user metadata and look it up by User ID. App Context is a durable profile store, not the graph itself. The follow relationship still lives in channel group membership, exactly as this page has described it.
Read the feed's history across many channels
The live feed delivers a status update the moment it's published. Retrieving history is less direct than for a single channel, because Message Persistence fetches history per channel, not per channel group. Reconstructing a merged, chronological feed for user A means fetching each followed person's status channel and merging the results yourself, client-side or server-side. There's no single call that returns a channel group's combined history pre-merged.
Next steps
- Presence. Join, leave, timeout, and how presence differs from a stored membership.
- Core concepts. The channel group model this pattern depends on, and its current limits.
- App Context. Store profile data to decorate the roster with names and avatars.
- Access Manager. Grant
manageon channel groups to your server only, andreadto clients. - Message Actions. Attach reactions or comments to a status update without a separate channel.
- Architectural choices. Why channel groups replace one subscription per followed user.