// Follow a user
let timeline = client.feed(group: "timeline", id: "john")
_ = try await timeline.follow(FeedId(group: "user", id: "tom"))
// Follow a stock
_ = try await timeline.follow(FeedId(group: "stock", id: "apple"))
// Follow with more fields
_ = try await timeline.follow(
FeedId(group: "stock", id: "apple"),
custom: ["reason": "investment"]
)
Activity Feeds V3 is in closed alpha — do not use it in production (just yet).
Follows
Follow & Unfollow
// Follow a user
val timeline = client.feed(group = "timeline", id = "john")
timeline.follow(FeedId(group = "user", id = "tom"))
// Follow a stock
timeline.follow(FeedId(group = "stock", id = "apple"))
// Follow with more fields
timeline.follow(
targetFid = FeedId(group = "stock", id = "apple"),
custom = mapOf("reason" to "investment")
)
const timeline = client.feed("timeline", "john");
await timeline.getOrCreate();
// Follow a user
await timeline.follow("user:tom");
// Follow a stock
await timeline.follow("stock:apple");
// Follow with more fields
await timeline.follow("stock:apple", {
push_preference: "all",
custom: {
reason: "investment",
},
});
// Follow a user
final timeline = client.feed(group: 'timeline', id: 'john');
await timeline.follow(targetFid: const FeedId(group: 'user', id: 'tom'));
// Follow a stock
await timeline.follow(targetFid: const FeedId(group: 'stock', id: 'apple'));
// Follow with more fields
await timeline.follow(
targetFid: const FeedId(group: 'stock', id: 'apple'),
custom: {'reason': 'investment'},
);
const timeline = client.feeds.feed("timeline", "john");
await timeline.getOrCreate({
user_id: "john",
});
// Follow a user
await client.feeds.follow({
source: timeline.feed,
target: "user:tom",
});
// Follow a stock
await client.feeds.follow({
source: timeline.feed,
target: "stock:apple",
});
// Follow with more fields
await client.feeds.follow({
source: timeline.feed,
target: "stock:apple",
push_preference: "all",
custom: {
reason: "investment",
},
});
// Create timeline feed
timeline := client.Feeds().Feed("timeline", "john")
_, err = timeline.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
UserID: getstream.PtrTo("john"),
})
if err != nil {
log.Fatal("Error getting/creating timeline feed:", err)
}
log.Println("Timeline feed created/retrieved successfully")
// Follow a user
_, err = client.Feeds().Follow(context.Background(), &getstream.FollowRequest{
Source: "timeline:john",
Target: "user:tom",
})
if err != nil {
log.Fatal("Error following user:", err)
}
log.Println("Successfully followed user:tom")
// Follow a stock
_, err = client.Feeds().Follow(context.Background(), &getstream.FollowRequest{
Source: "timeline:john",
Target: "stock:apple",
})
if err != nil {
log.Fatal("Error following stock:", err)
}
log.Println("Successfully followed stock:apple")
// Follow with more fields
_, err = client.Feeds().Follow(context.Background(), &getstream.FollowRequest{
Source: "timeline:john",
Target: "stock:apple",
PushPreference: getstream.PtrTo("all"),
Custom: map[string]any{
"reason": "investment",
},
})
if err != nil {
log.Fatal("Error following stock with custom fields:", err)
}
log.Println("Successfully followed stock:apple with custom fields")
UnfollowRequest unfollowRequest = UnfollowRequest.builder().build();
UnfollowResponse response =
feeds
.unfollow(USER_FEED_TYPE + testUserId, USER_FEED_TYPE + testUserId2, unfollowRequest)
.execute()
.getData();
$response = $this->feedsV3Client->unfollow(
self::USER_FEED_TYPE . $this->testUserId,
self::USER_FEED_TYPE . $this->testUserId2
);
var response = await _feedsV3Client.UnfollowAsync(
$"user:{_testFeedId}",
$"user:{_testFeedId3}",
new { user_id = _testUserId }
);
response = self.client.feeds.unfollow(
f"{self.USER_FEED_TYPE}:{self.test_user_id}",
f"{self.USER_FEED_TYPE}:{self.test_user_id_2}",
)
Querying Follows
// Do I follow a list of feeds
// My feed is timeline:john
let followQuery = FollowsQuery(
filter: .and([
.equal(.sourceFeed, "timeline:john"),
.in(.targetFeed, ["user:sara", "user:adam"])
])
)
let followList = client.followList(for: followQuery)
let page1 = try await followList.get()
let page2 = try await followList.queryMoreFollows()
let page1And2 = followList.state.follows
// Paginating through followers for a feed
// My feed is timeline:john
let followerQuery = FollowsQuery(
filter: .equal(.targetFeed, "timeline:john")
)
let followerList = client.followList(for: followerQuery)
let followerPage1 = try await followerList.get()
// Do I follow a list of feeds
// My feed is timeline:john
val followQuery = FollowsQuery(
filter = Filters.and(
Filters.equal("source_feed", "timeline:john"),
Filters.`in`("target_feed", listOf("user:sara", "user:adam"))
)
)
val followList = client.followList(query = followQuery)
val page1: Result<List<FollowData>> = followList.get()
val page2: Result<List<FollowData>> = followList.queryMoreFollows()
val page1And2 = followList.state.follows
// Paginating through followers for a feed
// My feed is timeline:john
val followerQuery = FollowsQuery(
filter = Filters.equal("target_feed", "timeline:john")
)
val followerList = client.followList(query = followerQuery)
val followerPage1: Result<List<FollowData>> = followerList.get()
const myTimeline = client.feed("timeline", "john");
await myTimeline.getOrCreate();
// Do I follow a list of feeds
const response = await client.queryFollows({
filter: {
source_feed: myTimeline.feed,
target_feed: { $in: ["user:sara", "user:adam"] },
},
});
console.log(response.follows);
const userFeed = client.feed("user", "john");
await userFeed.getOrCreate();
// Paginating through followers for a feed - won't store followers in state
const firstPage = await userFeed.queryFollowers({
limit: 20,
});
// Next page - won't store followers in state
const secondPage = await userFeed.queryFollowers({
limit: 20,
next: firstPage.next,
});
// or load when reading feed - will store followers in state
await feed.getOrCreate({
followers_pagination: {
limit: 10,
},
});
// and then load next pages (or first if followers are not yet loaded) - will store followers in state
await feed.loadNextPageFollowers({ limit: 10 });
console.log(feed.state.getLatestValue().followers);
// Filter by source - feeds that I follow - won't store followings in state
await myTimeline.queryFollowing({ limit: 10 });
// or load when reading feed - will store followings in state
await feed.getOrCreate({
following_pagination: {
limit: 10,
},
});
// and then load next pages (or first if followings are not yet loaded) - will store followings in state
await feed.loadNextPageFollowing({ limit: 10 });
console.log(feed.state.getLatestValue().following);
// Do I follow a list of feeds
// My feed is timeline:john
const followQuery = FollowsQuery(
filter: Filter.and([
Filter.equal(FollowsFilterField.sourceFeed, 'timeline:john'),
Filter.in_(FollowsFilterField.targetFeed, ['user:sara', 'user:adam']),
]),
);
final followList = client.followList(followQuery);
final page1 = await followList.get();
final page2 = await followList.queryMoreFollows();
final page1And2 = followList.state.follows;
// Paginating through followers for a feed
// My feed is timeline:john
const followerQuery = FollowsQuery(
filter: Filter.equal(FollowsFilterField.targetFeed, 'timeline:john'),
);
final followerList = client.followList(followerQuery);
final followerPage1 = await followerList.get();
const myTimeline = client.feeds.feed("timeline", "john");
await myTimeline.getOrCreate({
user_id: "john",
});
// Do I follow a list of feeds
const response = await client.feeds.queryFollows({
filter: {
source_feed: myTimeline.feed,
target_feed: { $in: ["user:sara", "user:adam"] },
},
});
console.log(response.follows);
const userFeed = client.feeds.feed("user", "john");
await userFeed.getOrCreate({
user_id: "john",
});
// Paginating through followers for a feed
const firstPage = await client.feeds.queryFollows({
filter: { target_feed: userFeed.feed },
limit: 20,
});
// Next page
const secondPage = await client.feeds.queryFollows({
filter: { target_feed: userFeed.feed },
limit: 20,
next: firstPage.next,
});
// Filter by source - feeds that I follow
await client.feeds.queryFollows({
filter: { source_feed: myTimeline.feed },
limit: 20,
});
ctx := context.Background()
// Create timeline feed
myTimeline := client.Feeds().Feed("timeline", "john")
_, err = myTimeline.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
UserID: getstream.PtrTo("john"),
})
if err != nil {
log.Fatal("Error creating timeline feed:", err)
}
// Query follows to check if we follow a list of feeds
response, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
Filter: map[string]any{
"source_feed": "timeline:john",
"target_feed": map[string]any{
"$in": []string{"user:sara", "user:adam"},
},
},
})
if err != nil {
log.Fatal("Error querying follows:", err)
}
log.Printf("Follows: %+v", response.Data.Follows)
// Create user feed
userFeed := client.Feeds().Feed("user", "john")
_, err = userFeed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
UserID: getstream.PtrTo("john"),
})
if err != nil {
log.Fatal("Error creating user feed:", err)
}
// Paginating through followers for a feed - first page
firstPage, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
Filter: map[string]any{
"target_feed": "user:john",
},
Limit: getstream.PtrTo(20),
})
if err != nil {
log.Fatal("Error querying first page of follows:", err)
}
// Next page
secondPage, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
Filter: map[string]any{
"target_feed": "user:john",
},
Limit: getstream.PtrTo(20),
Next: firstPage.Data.Next,
})
if err != nil {
log.Fatal("Error querying second page of follows:", err)
}
log.Printf("First page follows: %+v", firstPage.Data.Follows)
log.Printf("Second page follows: %+v", secondPage.Data.Follows)
// Filter by source - feeds that I follow
sourceFollows, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
Filter: map[string]any{
"source_feed": "timeline:john",
},
Limit: getstream.PtrTo(20),
})
if err != nil {
log.Fatal("Error querying source follows:", err)
}
log.Printf("Source follows: %+v", sourceFollows.Data.Follows)
FollowRequest followRequest =
FollowRequest.builder()
.source(USER_FEED_TYPE + testUserId)
.target(USER_FEED_TYPE + testUserId2)
.build();
SingleFollowResponse response = feeds.follow(followRequest).execute().getData();
$response = $this->feedsV3Client->follow(
new GeneratedModels\FollowRequest(
source: self::USER_FEED_TYPE . $this->testUserId,
target: self::USER_FEED_TYPE . $this->testUserId2
)
);
var response = await _feedsV3Client.FollowAsync(
new FollowRequest
{
Source = $"user:{_testFeedId}",
Target = $"user:{_testFeedId2}"
}
);
response = self.client.feeds.follow(
source=f"{self.USER_FEED_TYPE}:{self.test_user_id}",
target=f"{self.USER_FEED_TYPE}:{self.test_user_id_2}",
)
Follows Queryable Built-In Fields
name | type | description | supported operations | example |
---|---|---|---|---|
source_feed | string or list of strings | The feed ID that is following | $in , $eq | { source_feed: { $eq: 'messaging:general' } } |
target_feed | string or list of strings | The feed ID being followed | $in , $eq | { target_feed: { $in: [ 'sports:news', 'tech:updates' ] } } |
status | string or list of strings | The follow status | $in , $eq | { status: { $in: [ 'accepted', 'pending', 'rejected' ] } } |
created_at | string, must be formatted as an RFC3339 timestamp | The time the follow relationship was created | $eq , $gt , $gte , $lt , $lte | { created_at: { $gte: '2023-12-04T09:30:20.45Z' } } |
Follow Requests
Some apps require the user’s approval for following them.
// Sara needs to configure the feed with visibility = followers for enabling follow requests
let saraFeed = saraClient.feed(
for: .init(
group: "user",
id: "sara",
data: .init(visibility: .followers)
)
)
try await saraFeed.getOrCreate()
// Adam requesting to follow the feed
let adamTimeline = adamClient.feed(group: "timeline", id: "adam")
try await adamTimeline.getOrCreate()
let followRequest = try await adamTimeline.follow(saraFeed.feed) // user:sara
print(followRequest.status) // .pending
// Sara accepting
try await saraFeed.acceptFollow(
adamTimeline.feed, // timeline:adam
role: "feed_member" // optional
)
// or rejecting the request
try await saraFeed.rejectFollow(adamTimeline.feed) // timeline:adam
// Sara needs to configure the feed with visibility = followers for enabling follow requests
val saraFeed = saraClient.feed(
query = FeedQuery(
group = "user",
id = "sara",
data = FeedInputData(visibility = FeedVisibility.Followers)
)
)
saraFeed.getOrCreate()
// Adam requesting to follow the feed
val adamTimeline = adamClient.feed(group = "timeline", id = "adam")
adamTimeline.getOrCreate()
val followRequest = adamTimeline.follow(saraFeed.fid) // user:sara
println(followRequest.getOrNull()?.status) // FollowStatus.PENDING
// Sara accepting
saraFeed.acceptFollow(
sourceFid = adamTimeline.fid, // timeline:adam
role = "feed_member" // optional
)
// or rejecting the request
saraFeed.rejectFollow(adamTimeline.fid) // timeline:adam
const saraFeed = saraClient.feed("user", uuidv4());
await saraFeed.getOrCreate({
// You need to set followers visibility to have follow requests
data: { visibility: "followers" },
});
const adamTimeline = adamClient.feed("timeline", uuidv4());
await adamTimeline.getOrCreate();
const followRequest = await adamTimeline.follow(saraFeed.feed);
console.log(followRequest.follow.status); // pending
await saraClient.acceptFollow({
source: adamTimeline.feed,
target: saraFeed.feed,
// Optionally provide role
follower_role: "feed_member",
});
await saraClient.rejectFollow({
source: adamTimeline.feed,
target: saraFeed.feed,
});
// Sara needs to configure the feed with visibility = followers for enabling follow requests
const saraFeedQuery = FeedQuery(
fid: FeedId(group: 'user', id: 'sara'),
data: FeedInputData(visibility: FeedVisibility.followers),
);
final saraFeed = saraClient.feedFromQuery(saraFeedQuery);
await saraFeed.getOrCreate();
// Adam requesting to follow the feed
final adamTimeline = adamClient.feed(group: 'timeline', id: 'adam');
await adamTimeline.getOrCreate();
final followRequest =
await adamTimeline.follow(targetFid: saraFeed.fid); // user:sara
print(followRequest.getOrNull()?.status); // .pending
// Sara accepting
await saraFeed.acceptFollow(
sourceFid: adamTimeline.fid, // timeline:adam
role: 'feed_member', // optional
);
// or rejecting the request
await saraFeed.rejectFollow(sourceFid: adamTimeline.fid); // timeline:adam
const saraFeed = client.feeds.feed("user", uuidv4());
await saraFeed.getOrCreate({
// You need to set followers visibility to have follow requests
data: { visibility: "followers" },
user_id: 'sara'
});
const adamTimeline = client.feeds.feed("timeline", uuidv4());
await adamTimeline.getOrCreate({
user_id:
});
const followRequest = await client.feeds.follow({
source: adamTimeline.feed,
target: saraFeed.feed,
});
console.log(followRequest.follow.status); // pending
await client.feeds.acceptFollow({
source: adamTimeline.feed,
target: saraFeed.feed,
// Optionally provide role
follower_role: "feed_member",
});
await client.feeds.rejectFollow({
source: adamTimeline.feed,
target: saraFeed.feed,
});
ctx := context.Background()
saraFeed := client.Feeds().Feed("user", "sara")
_, err = saraFeed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
Data: &getstream.FeedInput{
Visibility: getstream.PtrTo("followers"),
},
UserID: getstream.PtrTo("sara"),
})
if err != nil {
log.Fatal("Error creating sara feed:", err)
}
adamTimeline := client.Feeds().Feed("timeline", "adam")
_, err = adamTimeline.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
UserID: getstream.PtrTo("adam"),
})
if err != nil {
log.Fatal("Error creating adam timeline feed:", err)
}
// Create follow request from adamTimeline to saraFeed
followRequest, err := client.Feeds().Follow(ctx, &getstream.FollowRequest{
Source: "timeline:adam",
Target: "user:sara",
})
if err != nil {
log.Fatal("Error creating follow request:", err)
}
fmt.Printf("Follow request status: %s\n", followRequest.Data.Follow.Status)
// Accept follow request and set follower's role
_, err = client.Feeds().AcceptFollow(ctx, &getstream.AcceptFollowRequest{
Source: "timeline:adam",
Target: "user:sara",
FollowerRole: getstream.PtrTo("feed_member"),
})
if err != nil {
log.Fatal("Error accepting follow request:", err)
}
// Reject follow request
_, err = client.Feeds().RejectFollow(ctx, &getstream.RejectFollowRequest{
Source: "timeline:adam",
Target: "user:sara",
})
if err != nil {
log.Fatal("Error rejecting follow request:", err)
}
FollowRequest followRequest =
FollowRequest.builder()
.source(USER_FEED_TYPE + testUserId)
.target(USER_FEED_TYPE + testUserId2)
.build();
SingleFollowResponse response = feeds.follow(followRequest).execute().getData();
$response = $this->feedsV3Client->follow(
new GeneratedModels\FollowRequest(
source: self::USER_FEED_TYPE . $this->testUserId,
target: self::USER_FEED_TYPE . $this->testUserId2
)
);
var response = await _feedsV3Client.FollowAsync(
new FollowRequest
{
Source = $"user:{_testFeedId}",
Target = $"user:{_testFeedId2}"
}
);
response = self.client.feeds.follow(
source=f"{self.USER_FEED_TYPE}:{self.test_user_id}",
target=f"{self.USER_FEED_TYPE}:{self.test_user_id_2}",
)
Push Preferences on Follow
When following a feed, you can set push_preference
to control push notifications for future activities from that feed:
all
- Receive push notifications for all activities from the followed feednone
(default) - Don’t receive push notifications for activities from the followed feed
Note: The push_preference
parameter controls future notifications from the followed feed, while skip_push
controls whether the follow action itself triggers a notification.
Examples: Push Preferences vs Skip Push
Understanding the difference between push_preference
and skip_push
:
// Scenario 1: Follow a user and receive notifications for their future activities
await timeline.follow("user:alice", {
push_preference: "all", // You'll get push notifications for Alice's future posts
});
// Scenario 2: Follow a user but don't get notifications for their activities
await timeline.follow("user:bob", {
push_preference: "none", // You won't get push notifications for Bob's future posts
});
// Scenario 3: Follow a user silently
await timeline.follow("user:charlie", {
skip_push: true, // Charlie won't get a "you have a new follower" notification
push_preference: "all", // But you'll still get notifications for Charlie's future posts
});
// Scenario 4: Silent follow with no future notifications
await timeline.follow("user:diana", {
skip_push: true, // Diana won't know you followed her
push_preference: "none", // And you won't get notifications for her posts
});