Quick Start

Stream lets you build activity feeds at scale. The largest apps using Stream have over 100 million users. Stream Activity Feeds scales effectively while giving you more flexibility over the content shown in your feed.

Stream Feeds has two halves. Feed groups, ranking, permissions and push setup are configured on your backend with a server-side SDK, authenticated with your API secret. The code you ship to your users is a client SDK, and it connects with a token signed on that backend. Whichever half you are here for, this sidebar carries an overview of the other.

Start here

Getting started

For SDK-accurate examples generated from the OpenAPI spec, use the Stream snippets reference: TypeScript SDK - Feeds API. Replace node in the URL with the SDK language you want to inspect.

// Initialize the client and connect
val client = FeedsClient(
    context = context,
    apiKey = StreamApiKey.fromString("<your_api_key>"),
    user = User(id = "john"),
    tokenProvider = object : StreamTokenProvider {
        override suspend fun loadToken(userId: StreamUserId): StreamToken {
            return StreamToken.fromString("<user_token>")
        }
    }
)
val connectResult: Result<StreamConnectedUser> = client.connect()

// Create a feed (or get its data if it exists)
val feed = client.feed(group = "user", id = "john")
feed.getOrCreate()

// Add an activity
val result: Result<ActivityData> = feed.addActivity(
    request = FeedAddActivityRequest(
        type = "post",
        text = "Hello, Stream Feeds!",
    )
)

Key concepts

Activities

Activities are the core content units in Stream Feeds. They can represent posts, photos, videos, polls, and any custom content type you define.

Feeds

Feeds are collections of activities. They can be personal feeds, timeline feeds, notification feeds, or custom feeds for your specific use case.

Real-time updates

Stream Feeds provides real-time updates through WebSocket connections, ensuring your app stays synchronized with the latest content.

Social features

Built-in support for reactions, comments, bookmarks, and polls makes it easy to build engaging social experiences.

Server-side vs client-side

Most API calls can be made client-side. Client-side API calls use the permission system. Server-side API calls have full access.

flowchart LR
  subgraph app[Your app]
    client[Client SDK]
  end
  subgraph backend[Your backend]
    server[Server-side SDK]
  end
  client <--> api[Stream API]
  server --> api
  server -. user token .-> client

Most apps will default to making API calls client-side. Engineers will often use server-side to do the following tasks:

  • Updating feed groups or feed views for ranking and aggregation
  • Syncing users
  • Returning tokens for authenticating users
  • Writing to feeds that do not belong to the current user (since the user does not have access client side to do this)

Common use cases

Social media feed

val timeline = client.feed(group = "timeline", id = "john")
timeline.getOrCreate()

// Add a reaction to an activity
val addReactionResult: Result<FeedsReactionData> = timeline.addActivityReaction(
    activityId = "activity_123",
    request = AddReactionRequest(type = "like")
)

// Add a comment to an activity
val addCommentResult: Result<CommentData> = timeline.addComment(
    request = ActivityAddCommentRequest(
        comment = "Great post!",
        activityId = "activity_123",
    )
)

// Add a reaction to a comment
val addCommentReactionResult: Result<FeedsReactionData> = timeline.addCommentReaction(
    commentId = "comment_456",
    request = AddCommentReactionRequest(type = "like")
)

Notification feed

// Create a notification feed
val notifications = client.feed(group = "notification", id = "john")
notifications.getOrCreate()

// Mark notifications as read
notifications.markActivity(
    request = MarkActivityRequest(markAllRead = true)
)

Polls

// Create a poll
val feedId = FeedId(group = "user", id = "john")
val feed = client.feed(fid = feedId)
val request = CreatePollRequest(
    name = "What's your favorite color?",
    options = listOf(
        PollOptionInput(text = "Red"),
        PollOptionInput(text = "Blue"),
        PollOptionInput(text = "Green")
    )
)
val activityData: Result<ActivityData> = feed.createPoll(
    request = request,
    activityType = "poll"
)

// Vote on a poll
val activity = client.activity(
    activityId = activityData.getOrNull()?.id ?: "",
    fid = feedId
)
val pollVoteData: Result<PollVoteData?> = activity.castPollVote(
    request = CastPollVoteRequest(
        vote = VoteData(optionId = "option_456")
    )
)

Advanced features

Custom activity types

Create custom activity types to represent your app's specific content:

val workoutActivity: Result<ActivityData> = feed.addActivity(
    request = FeedAddActivityRequest(
        custom = mapOf(
            "distance" to 5.2,
            "duration" to 1800,
            "calories" to 450
        ),
        text = "Just finished my run",
        type = "workout"
    )
)

Real-time updates with the state layer

Only for client-side SDKs

class FeedViewModel(private val feed: Feed) : ViewModel() {
    val activities: StateFlow<List<ActivityData>> = feed.state.activities

    init {
        setupRealtimeUpdates()
    }

    private fun setupRealtimeUpdates() {
        viewModelScope.launch {
            val result: Result<FeedData> = feed.getOrCreate()
            // handle result
        }
    }
}

What's next