const myFeedGroup = await serverClient.feeds.createFeedGroup({
id: "myid",
activity_selectors: [
{
type: "following",
},
],
ranking: { type: "time" },
activity_processors: [
{
type: "topic",
},
],
custom: {
description: "My custom feed group",
},
});
Intro & Defaults
Creating Feed Groups & Defaults
There are several feed groups setup by default.
Group | Description |
---|---|
user | A feed setup for the content a user creates. Typically you add activities here when someone writes a post |
timeline | The timeline feed is used when you’re following. So if user Charlie is following John, timeline:charlie would follow user:john |
foryou | A version of the timeline feed that adds popular content, and priorities popularity over recency |
notification | A notification feed. Think of the bell icon you see in most apps |
You can also create your own feed group. Here’s how to create a feed group using the API:
package main
import (
"context"
"log"
"fmt"
"time"
"github.com/GetStream/getstream-go/v3"
)
func main() {
client, err := getstream.NewClient("<your_api_key>", "<your_api_secret>")
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
feedsClient := client.Feeds()
// Create a following view for the feed group
followingViewID := fmt.Sprintf("following-view-%d", time.Now().Unix())
followingView, err := feedsClient.CreateFeedView(ctx, &getstream.CreateFeedViewRequest{
ViewId: &followingViewID,
ActivitySelectors: []getstream.ActivitySelector{
{
Type: getstream.PtrTo("following"),
},
},
Ranking: &getstream.Ranking{
Type: getstream.PtrTo("time"),
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("Following view created: %s", *followingView.Data.ViewId)
// Create custom feed group with the view
myFeedGroup, err := feedsClient.CreateFeedGroup(ctx, &getstream.CreateFeedGroupRequest{
FeedGroupId: getstream.PtrTo("myid"),
DefaultViewId: followingView.Data.ViewId,
ActivityProcessors: []getstream.ActivityProcessor{
{
Type: getstream.PtrTo("topic"),
},
},
Custom: map[string]interface{}{
"description": "My custom feed group",
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("Custom feed group created: %+v", myFeedGroup.Data)
// Create aggregation view for notifications
aggregationViewID := fmt.Sprintf("aggregation-view-%d", time.Now().Unix())
aggregationView, err := feedsClient.CreateFeedView(ctx, &getstream.CreateFeedViewRequest{
ViewId: &aggregationViewID,
Aggregation: &getstream.AggregationConfig{
Format: getstream.PtrTo("{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"),
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("Aggregation view created: %s", *aggregationView.Data.ViewId)
// Create notification feed group
notificationFeedGroup, err := feedsClient.CreateFeedGroup(ctx, &getstream.CreateFeedGroupRequest{
FeedGroupId: getstream.PtrTo("mynotifications"),
DefaultViewId: aggregationView.Data.ViewId,
Notification: &getstream.NotificationConfig{
TrackRead: getstream.PtrTo(true),
TrackSeen: getstream.PtrTo(true),
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("Notification feed group created: %+v", notificationFeedGroup.Data)
}
Activity Ranking
When ranking activities you can specify a ranking formula.
const response = await serverClient.feeds.createFeedGroup({
id: "mytimeline",
ranking: { type: "expression", score: "decay_linear(time) * popularity" },
activity_selectors: [
{
type: "following",
},
],
});
$this->feedsV3Client->createFeedGroup(
new GeneratedModels\CreateFeedGroupRequest(
id: $ranked_group,
defaultVisibility: 'public',
ranking: new GeneratedModels\RankingConfig(
type: 'default',
score: 'decay_linear(time) * popularity'
)
)
);
await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
ID = "ranked-group",
DefaultVisibility = "public",
Ranking = new RankingConfig
{
Type = "default",
Score = "decay_linear(time) * popularity"
}
});
self.client.feeds.create_feed_group(
id=feed_group_id,
default_visibility="public",
ranking=RankingConfig(
type="default",
score="decay_linear(time) * popularity"
)
)
For you Feeds
Many apps want to have a “for you” or personalized feed. There are a couple benefits to a personalized feed:
- Works well even if your users don’t spend much time setting up follows
- Can be a mechanism to discover new content or things to follow
Stream offers a few built-in methods to create a for you feed and gives you the API access to do more advanced customization if needed.
This next example is a bit more complicated. It uses an activity processor to add topic data to activities and ranks content you’re likely to engage with higher in the feed.
const response = await serverClient.feeds.createFeedGroup({
id: "mytimeline",
// Run the activity processors to analyse topics for text & images
activity_processors: [{ type: "image_topic" }, { type: "text_topic" }],
// Activity selectors change which activities are included in the feed
// The default "following" selectors gets activities from the feeds you follow
// The "popular" activity selectors includes the popular activities
// And "interest" activities similar to activities you've engaged with in the past
// You can use multiple selectors in 1 feed
activity_selectors: [
{
type: "popular",
},
{
type: "following",
},
{
type: "interest",
},
],
// Rank for a user based on interest score
// This calculates a score 0-1.0 of how well the activity matches the user's prior interest
ranking: {
type: "interest",
score: "decay_linear(time) * interest * decay_linear(popularity)",
},
});
const forYouFeed = client.feeds.feed("mytimeline", "thierry");
const response = await forYouFeed.getOrCreate({ user_id: "thierry" });
package main
import (
"context"
"log"
"fmt"
"time"
"github.com/GetStream/getstream-go/v3"
)
func main() {
client, err := getstream.NewClient("<your_api_key>", "<your_api_secret>")
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
feedsClient := client.Feeds()
// Create interest-based "For You" feed view
forYouViewID := fmt.Sprintf("for-you-view-%d", time.Now().Unix())
view, err := feedsClient.CreateFeedView(ctx, &getstream.CreateFeedViewRequest{
ViewId: &forYouViewID,
// Activity selectors change which activities are included in the feed
// The default "following" selectors gets activities from the feeds you follow
// The "popular" activity selectors includes the popular activities
// And "interest" activities similar to activities you've engaged with in the past
// You can use multiple selectors in 1 feed
ActivitySelectors: []getstream.ActivitySelector{
{
Type: getstream.PtrTo("popular"),
},
{
Type: getstream.PtrTo("following"),
},
{
Type: getstream.PtrTo("interest"),
},
},
// Rank for a user based on interest score
// This calculates a score 0-1.0 of how well the activity matches the user's prior interest
Ranking: &getstream.Ranking{
Type: getstream.PtrTo("interest"),
Score: getstream.PtrTo("decay_linear(time) * interest * decay_linear(popularity)"),
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("For You view created: %s", *view.Data.ViewId)
// Create feed group with activity processors for topic analysis
response, err := feedsClient.CreateFeedGroup(ctx, &getstream.CreateFeedGroupRequest{
FeedGroupId: getstream.PtrTo("mytimeline"),
DefaultViewId: view.Data.ViewId,
// Run the activity processors to analyse topics for text & images
ActivityProcessors: []getstream.ActivityProcessor{
{
Type: getstream.PtrTo("image_topic"),
},
{
Type: getstream.PtrTo("text_topic"),
},
},
Custom: map[string]interface{}{
"description": "Interest-based For You feed with topic analysis",
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("For You feed group created: %+v", response.Data)
// Read the personalized "For You" feed
// Activities will include following, popular and similar (via interest) activities
// Sorted by time decay, interest and popularity
forYouFeed := feedsClient.Feed("mytimeline", "thierry")
feedResponse, err := forYouFeed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
UserID: getstream.PtrTo("thierry"),
})
if err != nil {
log.Fatal(err)
}
log.Printf("For You feed loaded with %d activities", len(feedResponse.Data.Activities))
log.Println("Activities include following, popular and interest-based content")
}
Aggregation & Notification Feeds
Aggregation groups similar activities together, which is useful for notification feeds and reducing noise.
Aggregation Format
You can create your own aggregated feeds:
const myNotificationGrpup = await serverClient.feeds.createFeedGroup({
id: "myid",
// Group by activity type and day
aggregation: { format: '{{ type }}-{{ time.strftime("%Y-%m-%d") }}' },
// Enable notification tracking
notification: {
track_read: true,
track_seen: true,
},
});
$this->feedsV3Client->createFeedGroup(
new GeneratedModels\CreateFeedGroupRequest(
id: $group,
defaultVisibility: 'public',
activityProcessors: [
['type' => 'default']
],
aggregation: new GeneratedModels\AggregationConfig('{{ type }}-{{ time.strftime("%Y-%m-%d") }}')
)
);
await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
ID = "aggregated-group",
DefaultVisibility = "public",
ActivityProcessors = new List<ActivityProcessorConfig>
{
new() { Type = "default" }
},
Aggregation = new AggregationConfig
{
Format = "{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"
}
});
self.client.feeds.create_feed_group(
id = feed_group_id,
default_visibility="public",
activity_processors=[
ActivityProcessorConfig(type="default")
],
aggregation=AggregationConfig(
format="{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"
)
)
Notification Feed Example
The built-in notification
feed comes with the necessary configurations:
let notificationFeed = client.feed(group: "notification", id: "jane")
let notifications = try await notificationFeed.getOrCreate()
val notificationFeed = client.feed(group = "notification", id = "jane")
val notifications = notificationFeed.getOrCreate()
const notificationFeed = client.feed("notification", "jane");
// Read notifications
const notifications = (
await notificationFeed.getOrCreate({
limit: 20,
})
).aggregated_activities;
const notificationFeed = client.feed("notification", "jane");
// Read notifications
const notifications = (
await notificationFeed.getOrCreate({
limit: 20,
user_id: "<user_id>",
})
).aggregated_activities;
feedsClient := client.Feeds()
_, err = feedsClient.GetOrCreateFeed(ctx, userFeedType, testUserID, &getstream.GetOrCreateFeedRequest{
UserID: &testUserID,
})
if err != nil {
t.Logf("Failed to create feed 1: %v", err)
}
_, err = feedsClient.GetOrCreateFeed(ctx, userFeedType, testUserID2, &getstream.GetOrCreateFeedRequest{
UserID: &testUserID2,
})
testFeed = new Feed("user", testUserId, feeds);
testFeed2 = new Feed("user", testUserId2, feeds);
GetOrCreateFeedRequest feedRequest1 =
GetOrCreateFeedRequest.builder().userID(testUserId).build();
GetOrCreateFeedRequest feedRequest2 =
GetOrCreateFeedRequest.builder().userID(testUserId2).build();
GetOrCreateFeedResponse feedResponse1 = testFeed.getOrCreate(feedRequest1).getData();
GetOrCreateFeedResponse feedResponse2 = testFeed2.getOrCreate(feedRequest2).getData();
testFeedId = feedResponse1.getFeed().getFeed();
testFeedId2 = feedResponse2.getFeed().getFeed();
$feedResponse1 = $this->testFeed->getOrCreateFeed(
new GeneratedModels\GetOrCreateFeedRequest(userID: $this->testUserId)
);
$feedResponse2 = $this->testFeed2->getOrCreateFeed(
new GeneratedModels\GetOrCreateFeedRequest(userID: $this->testUserId2)
);
var feedResponse1 = await _feedsV3Client.GetOrCreateFeedAsync(
FeedGroupID: "user",
FeedID: _testFeedId,
request: new GetOrCreateFeedRequest { UserID = _testUserId }
);
var feedResponse2 = await _feedsV3Client.GetOrCreateFeedAsync(
FeedGroupID: "user",
FeedID: _testFeedId2,
request: new GetOrCreateFeedRequest { UserID = _testUserId2 }
);
feed_response_1 = self.test_feed.get_or_create(user_id=self.test_user_id)
feed_response_2 = self.test_feed_2.get_or_create(
user_id=self.test_user_id_2
)
Marking Notifications as Read
notificationFeed.markActivity(
request = MarkActivityRequest(
// Mark all notifications as read...
markAllRead = true,
// ...or only selected ones
markRead = listOf(
/* group names to mark as read */
)
)
)
await notificationFeed.markActivity({
// Mark all notifications as read...
mark_all_read: true,
// ...or only selected ones
mark_read: [
/* group names to mark as read */
],
});
await notificationFeed.markActivity({
// Mark all notifications as read...
mark_all_read: true,
// ...or only selected ones
mark_read: [
/* group names to mark as read */
],
user_id: "<user id>",
});
Updating feed groups
It’s possible to update any custom or built-in feed group or view
await client.feeds.updateFeedGroup({
id: "<id of feed group to update>",
// Fields to update
});
Deleting feed groups
await serverClient.feeds.deleteFeedGroup({
id: "<feed group id to delete>",
});