Activity Feeds V3 is in closed alpha — do not use it in production (just yet).

Feeds

Creating a Feed

// Example 1
response, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})

if err != nil {
  log.Fatal("Error:", err)
}
log.Printf("Success: %+v\n", response)

// Example 2
response2, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
  Data: &getstream.FeedInput{
    Description: getstream.PtrTo("My personal feed"),
    Name:        getstream.PtrTo("John Hikes"),
    Visibility:  getstream.PtrTo("public"),
  }})
if err != nil {
  log.Fatal("Error creating advanced feed:", err)
}
log.Printf("Success: %+v\n", response2)

Reading a Feed

Here is a basic example of how to read a feed:

feed := client.Feeds().Feed("user", "john")
response, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error calling getOrCreate:", err)
}

feedData := response.Data.Feed
activities := response.Data.Activities
members := response.Data.Members

The response will contain the following data.

You have more options when reading a feed, let’s go over a few:

feed := client.Feeds().Feed("user", "jack")
request := &getstream.GetOrCreateFeedRequest{
  Limit:  intPtr(10),
  UserID: stringPtr("<user id>"),
  View:   stringPtr("myview"),
  Filter: map[string]any{
    "filter_tags": []string{"green"}, // filter activities with filter tag green
  },
  ExternalRanking: map[string]any{
    "user_score": 0.8, // additional data used for ranking
  },
  FollowersPagination: &getstream.PagerRequest{
    Limit: intPtr(10),
  },
  FollowingPagination: &getstream.PagerRequest{
    Limit: intPtr(10),
  },
  MemberPagination: &getstream.PagerRequest{
    Limit: intPtr(10),
  },
}
response, err := feed.GetOrCreate(context.Background(), request)

Feed Pagination

Here is how you can read the next page on the feed:

feed := client.Feeds().Feed("user", "john")

// First page
firstPage, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Limit:  getstream.PtrTo(10),
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error getting first page:", err)
}

// Second page request using next cursor
nextPage, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Next:   firstPage.Data.Next,
  Limit:  getstream.PtrTo(10),
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error getting next page:", err)
}

log.Printf("First page activities count: %d", len(firstPage.Data.Activities))
log.Printf("Next page activities count: %d", len(nextPage.Data.Activities))

Filtering Examples

Another common use case is filtering a feed. This is trickier than it seems. Keep in mind that for performance reasons feeds often have to be computed on write time. To allow for filtering we expose the following API.

ctx := context.Background()
// Create feed
feed := client.Feeds().Feed("user", "john")

// Create activities
activities := []getstream.ActivityRequest{
	{
		Feeds:      []string{"user:john"},
		Type:       "post",
		Text:       stringPtr("first"),
		FilterTags: []string{"green", "blue"},
		UserID:     stringPtr("john"),
	},
	{
		Feeds:      []string{"user:john"},
		Type:       "post",
		Text:       stringPtr("second"),
		FilterTags: []string{"yellow", "blue"},
		UserID:     stringPtr("john"),
	},
	{
		Feeds:      []string{"user:john"},
		Type:       "post",
		Text:       stringPtr("third"),
		FilterTags: []string{"orange"},
		UserID:     stringPtr("john"),
	},
}

// Upsert activities
_, err = client.Feeds().UpsertActivities(ctx, &getstream.UpsertActivitiesRequest{
	Activities: activities,
})
if err != nil {
	log.Fatal("Error upserting activities:", err)
}

// Get or create feed with filter
response, err := feed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
	Filter: map[string]any{
		"filter_tags": []string{"blue"},
	},
	UserID: stringPtr("john"),
})
if err != nil {
	log.Fatal("Error getting or creating feed:", err)
}

log.Printf("Feed response: %+v", len(response.Data.Activities))

The filter syntax also supports $or and $and, so here’s an example that’s a little more complicated:

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,
})

Feed Members

You can add and remove members to a feed. This is useful for building communities where a set of users can add content to the feed.

It’s not possible to set/update member role on client-side. Use server-side SDKs for this. When adding members client-side all new members will have feed_member role:

_, err = feed.UpdateFeedMembers(context.Background(), &getstream.UpdateFeedMembersRequest{
	Operation: "upsert",
	Members: []getstream.FeedMemberRequest{
		{
			UserID: "john",
			Role:   getstream.PtrTo("moderator"),
			Custom: map[string]any{
				"joined": "2024-01-01",
			},
		},
	},
})
if err != nil {
	log.Fatal(err)
}

// Remove members
_, err = feed.UpdateFeedMembers(context.Background(), &getstream.UpdateFeedMembersRequest{
	Operation: "remove",
	Members: []getstream.FeedMemberRequest{
		{
			UserID: "john",
		},
		{
			UserID: "jane",
		},
	},
})
if err != nil {
	log.Fatal(err)
}

// Set members (overwrites the list)
_, err = feed.UpdateFeedMembers(context.Background(), &getstream.UpdateFeedMembersRequest{
	Operation: "set",
	Members: []getstream.FeedMemberRequest{
		{
			UserID: "john",
			Role:   getstream.PtrTo("moderator"),
		},
	},
})
if err != nil {
	log.Fatal(err)
}

Feed members vs followers

Followers and members might seem like similar concepts, but they serve two different purposes with some key differences.

Followers can only be feeds (for example the timeline feed of Alice follows the user feed of Bob). Followers’ aim is to access the content of a feed they’re interested in and interact with it.

Members can only be users (for example Alice adds Bob as a member to her feed about “Travel Hacks”). The aim of feed members is usually to help out with admin tasks (helpful if you want to build apps similar to Facebook pages) or to decide what activities a user has access to using membership levels (for example Bob becomes a premium member in Alice’s community).

Member invites

You can invite members with the invite flag, where invited users can accept or reject the membership.

// Create feed
feed := client.Feeds().Feed("user", "alice")

// Update feed members with upsert operation
updateResponse, err := feed.UpdateFeedMembers(context.Background(), &getstream.UpdateFeedMembersRequest{
  Operation: "upsert",
  Members: []getstream.FeedMemberRequest{
    {
      UserID: "john",
      Role:   getstream.PtrTo("moderator"),
      Invite: getstream.PtrTo(true),
      Custom: map[string]any{
        "reason": "community builder",
      },
    },
  },
})
if err != nil {
  log.Fatal("Error updating feed members:", err)
}
log.Printf("Feed members updated: %+v", updateResponse)

// Accept feed member invite
acceptResponse, err := feed.AcceptFeedMemberInvite(context.Background(), &getstream.AcceptFeedMemberInviteRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error accepting feed member invite:", err)
}
log.Printf("Feed member invite accepted: %+v", acceptResponse)

// Reject feed member invite
rejectResponse, err := feed.RejectFeedMemberInvite(context.Background(), &getstream.RejectFeedMemberInviteRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error rejecting feed member invite:", err)
}
log.Printf("Feed member invite rejected: %+v", rejectResponse)

Query Feeds

Querying feeds allows you to do things like showing the list of communities you’ve joined.

Here’s an example of how to query feeds:

Querying My Feeds

ctx := context.Background()

// First page query
firstPage, err := client.Feeds().QueryFeeds(ctx, &getstream.QueryFeedsRequest{
  Filter: map[string]any{
    "created_by_id": "john",
  },
  Limit: getstream.PtrTo(10),
  Sort: []getstream.SortParamRequest{
    {
      Field:     getstream.PtrTo("created_at"),
      Direction: getstream.PtrTo(-1),
    },
  },
})
if err != nil {
  log.Fatal("Error querying first page:", err)
}

// Second page query using next cursor from first page
secondPage, err := client.Feeds().QueryFeeds(ctx, &getstream.QueryFeedsRequest{
  Filter: map[string]any{
    "created_by_id": "john",
  },
  Limit: getstream.PtrTo(10),
  Sort: []getstream.SortParamRequest{
    {
      Field:     getstream.PtrTo("created_at"),
      Direction: getstream.PtrTo(-1),
    },
  },
  Next: firstPage.Data.Next,
})
if err != nil {
  log.Fatal("Error querying second page:", err)
}

log.Printf("First page: %d feeds, next: %v", len(firstPage.Data.Feeds), firstPage.Data.Next)
log.Printf("Second page: %d feeds, next: %v", len(secondPage.Data.Feeds), secondPage.Data.Next)

Querying Feeds Where I Am a Member

ctx := context.Background()

filter := map[string]any{
  "members": map[string]any{
    "$in": []string{"john"},
  },
}

response, err := client.Feeds().QueryFeeds(ctx, &getstream.QueryFeedsRequest{
  Filter: filter,
})
if err != nil {
  log.Fatal("Error querying feeds:", err)
}

log.Printf("Query feeds response: %+v", response)

Querying feeds by name or visibility

// Create context
ctx := context.Background()

response1, err := client.Feeds().QueryFeeds(ctx, &getstream.QueryFeedsRequest{
  Filter: map[string]any{
    "visibility": map[string]any{
      "$eq": "public",
    },
    "name": map[string]any{
      "$q": "Sports",
    },
  },
})
if err != nil {
  log.Fatal("Error querying feeds:", err)
}
log.Printf("Query feeds response 1: %+v", response1)

response2, err := client.Feeds().QueryFeeds(ctx, &getstream.QueryFeedsRequest{
  Filter: map[string]any{
    "visibility": getstream.PtrTo("public"),
    "description": map[string]any{
      "$autocomplete": "tech",
    },
  },
})

Querying feeds by creator name

// Create context
ctx := context.Background()

// Search public feeds created by users with 'Thompson' in their name
if response, err := client.Feeds().QueryFeeds(ctx, &getstream.QueryFeedsRequest{
  Filter: map[string]any{
    "visibility": "public",
    "created_by.name": map[string]any{
      "$q": "Thompson",
    },
  },
});

err != nil {
  log.Fatal("Error querying feeds:", err)
} else {
  log.Printf("Found %d feeds", len(response.Data.Feeds))
}

Feeds Queryable Built-in Fields

nametypedescriptionsupported operationsexample
idstring or list of stringsThe ID of the feed$in, $eq{ id: { $in: [ 'abc', 'xyz' ] } }
group_idstring or list of stringsThe ID of the group this feed belongs to$in, $eq{ group_id: { $in: [ 'abc', 'xyz' ] } }
feedstring or list of stringsThe fully qualified feed ID (group_id:id)$in, $eq{ fid: { $in: [ 'abc', 'xyz' ] } }
visibilitystring or list of stringsThe visibility setting of the feed$in, $eq{ visibility: { $eq: 'public' } }
created_by_idstring or list of stringsThe ID of the user who created the feed$in, $eq{ created_by_id: { $in: [ 'abc', 'xyz' ] } }
created_by.namestringThe name of the user who created the feed$eq, $q, $autocomplete{ 'created_by.name': { $autocomplete: 'Frank' } }
namestringThe name of the feed$eq, $q, $autocomplete{ name: { $q: 'Sports' } }
descriptionstringThe description of the feed$eq, $q, $autocomplete{ description: { $autocomplete: 'tech' } }
member_countnumberThe number of members in this feed$eq, $ne, $gt, $lt, $gte, $lte{ member_count: { $gt: 100 } }
memberslist of stringsThe list of members in this feed$in{ members: { $in: [ 'bob', 'alice' ] } }
following_countnumberThe number of feeds this feed follows$eq, $ne, $gt, $lt, $gte, $lte{ following_count: { $gt: 100 } }
following_feedslist of stringsThe list of feeds this feed follows$in{ following_feeds: { $in: [ 'feed1', 'feed2' ] } }
follower_countnumberThe number of followers of this feed$eq, $ne, $gt, $lt, $gte, $lte{ follower_count: { $gt: 100 } }
created_atstring, RFC3339 timestampThe time the feed was created$eq, $gt, $lt, $gte, $lte{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }
updated_atstring, RFC3339 timestampThe time the feed was updated$eq, $gt, $lt, $gte, $lte{ updated_at: { $gte: '2023-12-04T09:30:20.45Z' } }
filter_tagslist of stringsTags for filtering the feed$eq, $contains, $in{ filter_tags: { $in: [ 'sports', 'news' ] } }

Feeds can be sorted by created_at, updated_at, member_count, follower_count, and following_count.

Be sure to reach out to support if you need additional query feed capabilities.

Query Feed Members

You can query the members of a feed. This is useful for showing the list of members in a community.

val memberList = client.memberList(
    MembersQuery(
        fid = FeedId(group = "stock", id = "apple"),
        filter = MembersFilterField.role.equal("moderator"),
    )
)
memberList.get()
memberList.state.members.collect { members ->
    // Handle members
}

Feed Members Queryable Built-in Fields

nametypedescriptionsupported operationsexample
user_idstring or list of stringsThe ID of the user who is a member of the feed$in, $eq{ user_id: { $eq: 'user_123' } }
rolestring or list of stringsThe role of the member$in, $eq{ role: { $in: [ 'admin', 'moderator', 'member' ] } }
statusstring or list of stringsThe membership status$in, $eq{ status: { $in: [ 'member', 'pending', 'rejected' ] } }
created_atstring, must be formatted as an RFC3339 timestampThe time the membership was created$eq, $gt, $gte, $lt, $lte{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }
updated_atstring, must be formatted as an RFC3339 timestampThe time the membership was last updated$eq, $gt, $gte, $lt, $lte{ updated_at: { $gte: '2023-12-04T09:30:20.45Z' } }
© Getstream.io, Inc. All Rights Reserved.