Activity Feeds v3 is in beta — try it out!

Feeds

Creating a Feed

When using server-side SDKs, the user_id parameter is required in the getOrCreate request. It’s needed in order to automatically create a user. Client-side SDKs do not require this parameter as the user is authenticated via token.

// 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)

Built-in feed groups

GroupDescription
userA feed setup for the content a user creates. Typically you add activities here when someone writes a post
timelineThe timeline feed is used when you’re following. So if user Charlie is following John, timeline:charlie would follow user:john
foryouA version of the timeline feed that adds popular content, and priorities popularity over recency
notificationA notification feed. Think of the bell icon you see in most apps
storyA feed set up for users to post story activities (activities with expiration data)
storiesA timeline feed which can be used to follow other users’ stories.

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

Filters

filter provides a performant way to read a feed, and only select activities that match a given filter.

Please note that filtering is typically used for fields with fixed value sets (for example filter_tags), for text based search, you should check out query activities endpoint

Examples

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:

// Get all the activities where filter tags contain both "green" and "orange"
feed := client.Feeds().Feed("user", "john")
response, err := feed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
    Filter: map[string]any{
        "$and": []map[string]any{
            {"filter_tags": []string{"green"}},
            {"filter_tags": []string{"orange"}},
        },
    },
    UserID: getstream.PtrTo("john"),
})
if err != nil {
    log.Fatal("Error getting feed with filter:", err)
}
log.Printf("Activities: %+v", response.Data.Activities)

Supported filters

What filters you can use when reading a feed depends on the feed group (or view, if provided) configuration.

The activity selectors page explains this in detail, but some quick examples: the user group uses current selector, and the timeline group the following selector by default.

The following filter options are available for the following selector:

nametypedescriptionsupported operationsexample
idstring or list of stringsThe ID of the activity$in, $eq{ id: { $in: [ 'abc', 'xyz' ] } }
filter_tagslist of stringsTags for filtering$eq, $contains{ filter_tags: { $in: [ 'categoryA', 'categoryB' ] } }

The following filter options are available for the current, popular, interest, proximity and query selectors:

nametypedescriptionsupported operationsexample
idstring or list of stringsThe ID of the activity$in, $eq{ id: { $in: [ 'abc', 'xyz' ] } }
activity_typestring or list of stringsThe type of the activity$in, $eq{ activity_type: { $in: [ 'abc', 'xyz' ] } }
user_idstring or list of stringsThe ID of the user who created the activity$in, $eq{ user_id: { $in: [ 'abc', 'xyz' ] } }
textstringThe text content of the activity$eq, $q, $autocomplete{ text: { $q: 'popularity' } }
search_dataobjectThe extra metadata for search indexing$contains, $path_exists{ search_data: { $contains: { 'category': 'sports', 'status': 'active' } } }
interest_tagslist of stringsTags for user interests$eq, $contains{ interest_tags: { $in: [ 'sports', 'music' ] } }
filter_tagslist of stringsTags for filtering$eq, $contains{ filter_tags: { $in: [ 'categoryA', 'categoryB' ] } }
created_atstring, must be formatted as an RFC3339 timestampThe time the activity was created$eq, $gt, $lt, $gte, $lte{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }
popularitynumberThe popularity score of the activity$eq, $ne, $gt, $lt, $gte, $lte{ popularity: { $gte: 70 } }
nearobjectIndicates the GEO point to search nearby activities.$eq{ near: { $eq: { lat: 40.0, lng: -74.0, distance: 200 } } }
within_boundsobjectIndicates the GEO bounds to search for activities within.$eq{ within_bounds: { $eq: { ne_lat: 40.0, ne_lng: -115.0, sw_lat: 32.0, sw_lng: -125.0 } } }

The filter syntax also supports $or and $and:

// Get all the activities where filter tags contain both "green" and "orange"
filter := map[string]any{
  "$and": []map[string]any{
    {"filter_tags": []string{"green"}},
    {"filter_tags": []string{"orange"}},
  },
}

When providing filter to read a feed, activity selector filters on group/view level are ignored.

Overview of built-in fields

GetOrCreateFeedResponse

NameTypeDescriptionConstraints
activitiesActivityResponse[]-Required
aggregated_activitiesAggregatedActivityResponse[]-Required
createdboolean-Required
durationstringDuration of the request in millisecondsRequired
feedFeedResponse-Required
followersFollowResponse[]-Required
followers_paginationPagerResponse--
followingFollowResponse[]-Required
following_paginationPagerResponse--
member_paginationPagerResponse--
membersFeedMemberResponse[]-Required
nextstring--
notification_statusNotificationStatusResponse--
pinned_activitiesActivityPinResponse[]-Required
prevstring--

FeedResponse

NameTypeDescriptionConstraints
created_atnumberWhen the feed was createdRequired
created_byUserResponseUser who created the feedRequired
customobjectCustom data for the feed-
deleted_atnumberWhen the feed was deleted-
descriptionstringDescription of the feedRequired
feedstringFully qualified feed ID (group_id:id)Required
filter_tagsstring[]Tags used for filtering feeds-
follower_countintegerNumber of followers of this feedRequired
following_countintegerNumber of feeds this feed followsRequired
group_idstringGroup this feed belongs toRequired
idstringUnique identifier for the feedRequired
member_countintegerNumber of members in this feedRequired
namestringName of the feedRequired
own_capabilitiesFeedOwnCapability[]Capabilities the current user has for this feed-
own_followsFollowResponse[]Follow relationships where the current user's feeds are following this feed-
own_membershipFeedMemberResponseMembership information for the current user in this feed-
pin_countintegerNumber of pinned activities in this feedRequired
updated_atnumberWhen the feed was last updatedRequired
visibilitystringVisibility setting for the feed-

Deleting a Feed

Deleting a feed is an irreversible operation. This goes for both soft and hard deletes.

Deleting a feed will cascade delete the following entities:

  • Follows - any follow relationship this feed is part of will be deleted
  • Feed members
  • Pinned activities
  • Activity marks (read/seen)
  • Activities - the following will be deleted for each activity
    • Reactions
    • Bookmarks
    • Comments - the following will be deleted for each comment
      • Reactions

The deletion of a feed will be done asynchronously. If the endpoint is called server side a task_id is returned and you can use this task ID to track the deletion process.

The difference between a hard and soft delete is that soft deleting will soft delete the entities that supports this. These are feeds, activities and comments. Soft deleted activities will retain their reactions and bookmarks until hard deleted. Soft deleted comments will retain their reactions until hard deleted. This means that this data can still be exported with the export endpoint.

// Soft delete a feed
response, err := feed.Delete(context.Background(), &getstream.DeleteFeedRequest{
    HardDelete: getstream.PtrTo(false),
})

// Hard delete a feed
response, err := feed.Delete(context.Background(), &getstream.DeleteFeedRequest{
    HardDelete: getstream.PtrTo(true),
})

// Check task progress (you need to poll GetTask)
taskResponse, err := client.GetTask(context.Background(), response.Data.TaskID, &getstream.GetTaskRequest{})
fmt.Println(taskResponse.Data.Status == "completed")
© Getstream.io, Inc. All Rights Reserved.