@nolag/feed

Activity feeds with posts, likes, comments, and fan-out delivery.

Overview

@nolag/feed powers real-time activity feeds for social apps, community platforms, and content aggregators. Posts, likes, and comments are delivered live to all subscribers of a channel and replayed for up to 30 days so users joining late always see a full timeline. Fan-out happens server-side: publish once to a channel and every subscriber receives the update instantly.

Key Features

  • Real-time post, like, and comment delivery to all channel subscribers
  • 30-day replay across all three topics: posts, reactions, and comments
  • Unread post tracking with per-channel badge counts
  • Unlike support with live tally updates
  • Nested comment threads per post
  • Automatic reconnection with channel and state restoration

How It Works

NoLagFeed wraps the @nolag/js-sdk client. Calling joinChannel() creates a FeedChannel that subscribes to three topics: posts for post creation events, reactions for like and unlike events, and comments for comment creation events. A PostStore accumulates posts and their reaction counts, while a CommentStore groups comments by post ID. An unread counter tracks posts that arrived while the user was away.

TopicPurposeReplay
postsPost creation events: text, media, author, timestamp30 days
reactionsLike and unlike events with running tally30 days
commentsComment creation events keyed by post ID30 days

Installation

npm install @nolag/feed @nolag/js-sdk

Quick Start

import { NoLagFeed } from '@nolag/feed'

// Create and connect the client
const feed = new NoLagFeed('your-access-token', {
  user: { id: 'user-123', name: 'Alice', avatar: '/img/alice.png' },
})

await feed.connect()

// Join a feed channel (e.g. a user timeline or community feed)
const channel = await feed.joinChannel('user-123-timeline')

// Listen for new posts in real time
channel.on('postCreated', (post) => {
  console.log(`${post.author.name}: ${post.text}`)
  console.log('Likes:', post.likeCount)
})

// Create a post
await channel.createPost({
  text: 'Just shipped a new feature!',
  media: [{ type: 'image', url: '/uploads/screenshot.png' }],
})

// Like a post
await channel.likePost('post-abc')

channel.on('postLiked', ({ postId, likeCount }) => {
  console.log(`Post ${postId} now has ${likeCount} likes`)
})

// Comment on a post
await channel.addComment('post-abc', 'Congrats!')

channel.on('commentAdded', ({ postId, comment }) => {
  console.log(`New comment on ${postId}: ${comment.text}`)
})

// Get unread count and mark as read
console.log('Unread:', channel.unreadCount)
await channel.markRead()

await feed.leaveChannel('user-123-timeline')
feed.disconnect()

API Reference

NoLagFeed

The main class. Manages the WebSocket connection, global user presence, and feed channel lifecycle.

MethodDescription
connect()Establish WebSocket connection and announce presence
disconnect()Gracefully close the connection and leave all channels
joinChannel(name)Join a feed channel; returns a FeedChannel instance
leaveChannel(name)Leave a channel and unsubscribe from its topics
getOnlineUsers()Return all users currently online across all joined channels

Events: NoLagFeed

EventPayloadDescription
connectednoneWebSocket connection established
disconnectedreason: stringConnection closed
reconnectednoneReconnection successful; channels are restored automatically
errorerror: ErrorUnrecoverable error occurred
userOnlineuser: FeedUserA user has come online
userOfflineuser: FeedUserA user has gone offline

FeedChannel

Returned by joinChannel(). Handles posts, reactions, comments, and unread tracking for a single channel.

Method / PropertyDescription
createPost(opts)Publish a new post with text, optional media array, and custom metadata
likePost(postId)Like a post; increments the like count for all subscribers
unlikePost(postId)Remove your like from a post
addComment(postId, text)Add a comment to the specified post
getPosts()Return all posts in the local store, newest first
getComments(postId)Return all comments for the specified post
unreadCountNumber of posts that arrived since markRead() was last called
markRead()Reset the unread count to zero

Events: FeedChannel

EventPayloadDescription
postCreatedFeedPostA new post arrived from another user
postSentFeedPostConfirmation that your own post was delivered
postLiked{ postId: string, likeCount: number, likedBy: string }A post received a new like
postUnliked{ postId: string, likeCount: number, unlikedBy: string }A like was removed from a post
commentAdded{ postId: string, comment: FeedComment }A comment was added to a post
commentSent{ postId: string, comment: FeedComment }Confirmation that your own comment was delivered
subscriberJoineduser: FeedUserA user joined this channel
subscriberLeftuser: FeedUserA user left this channel
replayStart{ count: number }Historical feed replay is beginning
replayEnd{ replayed: number }Historical feed replay is complete
unreadChanged{ count: number }The unread post count for this channel changed