{"id":21658555,"url":"https://github.com/GetStream/stream-go2","last_synced_at":"2025-07-17T21:31:19.916Z","repository":{"id":37493019,"uuid":"102585952","full_name":"GetStream/stream-go2","owner":"GetStream","description":"GetStream.io Go client","archived":false,"fork":false,"pushed_at":"2024-11-04T23:44:38.000Z","size":467,"stargazers_count":86,"open_issues_count":3,"forks_count":20,"subscribers_count":39,"default_branch":"main","last_synced_at":"2024-11-08T11:52:02.809Z","etag":null,"topics":["activity-feed","feed","getstream","news-feed","stream","stream-go2","timeline"],"latest_commit_sha":null,"homepage":"https://getstream.io","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GetStream.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-06T08:52:28.000Z","updated_at":"2024-06-25T15:09:37.000Z","dependencies_parsed_at":"2024-11-08T11:52:14.226Z","dependency_job_id":"f3316a45-5448-4439-995c-5e67b284f1b6","html_url":"https://github.com/GetStream/stream-go2","commit_stats":{"total_commits":291,"total_committers":27,"mean_commits":"10.777777777777779","dds":0.5567010309278351,"last_synced_commit":"c62983b8706ec00a31b940efc6982f893b67de60"},"previous_names":["reifcode/stream-go2"],"tags_count":71,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-go2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-go2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-go2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-go2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GetStream","download_url":"https://codeload.github.com/GetStream/stream-go2/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226304795,"owners_count":17603685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activity-feed","feed","getstream","news-feed","stream","stream-go2","timeline"],"created_at":"2024-11-25T09:29:27.927Z","updated_at":"2024-11-25T09:29:39.646Z","avatar_url":"https://github.com/GetStream.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Official Go SDK for [Stream Feeds](https://getstream.io/activity-feeds/) \u003c!-- omit in toc --\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"./assets/logo.svg\" width=\"50%\" height=\"50%\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    Official Go API client for Stream Feeds, a service for building applications with activity feeds.\n    \u003cbr /\u003e\n    \u003ca href=\"https://getstream.io/activity-feeds/docs/\"\u003e\u003cstrong\u003eExplore the docs »\u003c/strong\u003e\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"https://github.com/GetStream/stream-go2/issues\"\u003eReport Bug\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/GetStream/stream-go2/issues\"\u003eRequest Feature\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\u003e ## 🚨 Breaking change in v7.0 \u003c\n\u003e From version 7.0.0, all methods' first argument is `context.Context`. The reason is that we internally changed `http.NewRequest()` to `http.NewRequestWithContext()` so that it's easier to handle cancellations, timeouts and deadlines for our users.\n\n## About Stream\nstream-go2 is a Go client for [Stream](https://getstream.io) Feeds API.\n\nYou can sign up for a Stream account at our [Get Started](https://getstream.io/chat/get_started/) page.\n\nYou can use this library to access chat API endpoints server-side.\n\nFor the client-side integrations (web and mobile) have a look at the JavaScript, iOS and Android SDK libraries ([docs](https://getstream.io/activity-feeds/)).\n\n\u003e 💡 Note: this is a library for the **Feeds** product. The Chat SDKs can be found [here](https://getstream.io/chat/docs/).\n\n[![build](https://github.com/GetStream/stream-go2/workflows/build/badge.svg)](https://github.com/GetStream/stream-go2/actions)\n[![godoc](https://pkg.go.dev/badge/GetStream/stream-go2)](https://pkg.go.dev/github.com/GetStream/stream-go2/v8?tab=doc)\n[![Go Report Card](https://goreportcard.com/badge/github.com/GetStream/stream-go2)](https://goreportcard.com/report/github.com/GetStream/stream-go2)\n\n## Contents\n\n- [About Stream](#about-stream)\n- [Contents](#contents)\n- [Installation](#installation)\n  - [Creating a Client](#creating-a-client)\n  - [Rate Limits](#rate-limits)\n  - [Creating a Feed](#creating-a-feed)\n  - [Retrieving activities](#retrieving-activities)\n    - [Flat feeds](#flat-feeds)\n    - [Aggregated feeds](#aggregated-feeds)\n    - [Notification feeds](#notification-feeds)\n    - [Options](#options)\n  - [Adding activities](#adding-activities)\n  - [Updating activities](#updating-activities)\n  - [Partially updating activities](#partially-updating-activities)\n  - [Removing activities](#removing-activities)\n  - [Following another feed](#following-another-feed)\n    - [Options](#options-1)\n  - [Retrieving followers and followings](#retrieving-followers-and-followings)\n    - [Following](#following)\n    - [Followers](#followers)\n  - [Unfollowing a feed](#unfollowing-a-feed)\n  - [Updating an activity's `to` targets](#updating-an-activitys-to-targets)\n  - [Batch adding activities](#batch-adding-activities)\n  - [Batch creating follows](#batch-creating-follows)\n  - [Realtime tokens](#realtime-tokens)\n- [Analytics](#analytics)\n  - [Obtaining an Analytics client](#obtaining-an-analytics-client)\n  - [Tracking engagement](#tracking-engagement)\n  - [Tracking impressions](#tracking-impressions)\n  - [Email tracking](#email-tracking)\n- [Personalization](#personalization)\n- [Collections](#collections)\n- [Users](#users)\n- [Reactions](#reactions)\n- [Enrichment](#enrichment)\n- [License](#license)\n- [We are hiring!](#we-are-hiring)\n\n## Installation\n\nGet the client:\n\n```bash\n$ go get github.com/GetStream/stream-go2/v8\n```\n\n\u003e For `v4`, use `github.com/GetStream/stream-go2/v4` but updating to the last version is highly recommended.\n\n### Creating a Client\n\n```go\nimport stream \"github.com/GetStream/stream-go2/v8\"\n\nkey := \"YOUR_API_KEY\"\nsecret := \"YOUR_API_SECRET\"\n\nclient, err := stream.New(key, secret)\nif err != nil {\n    // ...\n}\n```\n\nYou can pass additional options when creating a client using the available `ClientOption` functions:\n\n```go\nclient, err := stream.NewClient(key, secret,\n    stream.WithAPIRegion(\"us-east\"),\n    stream.WithAPIVersion(\"1.0\"),\n    stream.WithTimeout(5 * time.Second),\n    ...,\n)\n```\n\nYou can also create a client using environment variables:\n\n```go\nclient, err := stream.NewFromEnv()\n```\n\nAvailable environment variables:\n\n* `STREAM_API_KEY`\n* `STREAM_API_SECRET`\n* `STREAM_API_REGION`\n* `STREAM_API_VERSION`\n\n### Rate Limits\n\nAPI has different rate limits for each distinct endpoint and this information is returned to the client in response headers and SDK parses headers into `Rate` type.\n\nThis info is provided to the user in 2 ways:\n* in responses; `resp, _ := feed.GetActivities; resp.Rate`.\n* in errors; if request doesn't succeed then error is a type of `APIError` and headers are accessible via `err.(APIError).Rate`.\n\n### Creating a Feed\n\nCreate a flat feed from slug and user ID:\n\n```go\nflat, err := client.FlatFeed(\"user\", \"123\")\n```\n\nCreate an aggregated feed from slug and user ID:\n\n```go\naggr, err := client.AggregatedFeed(\"aggregated\", \"123\")\n```\n\nCreate a notification feed from slug and user ID:\n\n```go\nnotif, err := client.NotificationFeed(\"notification\", \"123\")\n```\n\nFlat, aggregated, and notification feeds implement the `Feed` interface methods.\n\nIn the snippets below, `feed` indicates any kind of feed, while `flat`, `aggregated`, and `notification` are used\nto indicate that only that kind of feed has certain methods or can perform certain operations.\n\n### Retrieving activities\n\n#### Flat feeds\n\n```go\nresp, err := flat.GetActivities()\nif err != nil {\n    // ...\n}\n\nfmt.Println(\"Duration:\", resp.Duration)\nfmt.Println(\"Rate:\", resp.Rate)\nfmt.Println(\"Next:\", resp.Next)\nfmt.Println(\"Activities:\")\nfor _, activity := range resp.Results {\n    fmt.Println(activity)\n}\n```\n\nYou can retrieve flat feeds with [custom ranking](https://getstream.io/docs/#custom_ranking), using the dedicated method:\n\n```go\nresp, err := flat.GetActivitiesWithRanking(ctx, \"popularity\")\nif err != nil {\n    // ...\n}\n```\n\n#### Aggregated feeds\n\n```go\nresp, err := aggregated.GetActivities(ctx)\nif err != nil {\n    // ...\n}\n\nfmt.Println(\"Duration:\", resp.Duration)\nfmt.Println(\"Rate:\", resp.Rate)\nfmt.Println(\"Next:\", resp.Next)\nfmt.Println(\"Groups:\")\nfor _, group := range resp.Results {\n    fmt.Println(\"Group:\", group.Name, \"ID:\", group.ID, \"Verb:\", group.Verb)\n    fmt.Println(\"Activities:\", group.ActivityCount, \"Actors:\", group.ActorCount)\n    for _, activity := range group.Activities {\n        // ...\n    }\n}\n```\n\n#### Notification feeds\n\n```go\nresp, err := notification.GetActivities(ctx)\nif err != nil {\n    // ...\n}\n\nfmt.Println(\"Duration:\", resp.Duration)\nfmt.Println(\"Rate:\", resp.Rate)\nfmt.Println(\"Next:\", resp.Next)\nfmt.Println(\"Unseen:\", resp.Unseen, \"Unread:\", resp.Unread)\nfmt.Println(\"Groups:\")\nfor _, group := range resp.Results {\n    fmt.Println(\"Group:\", group.Group, \"ID:\", group.ID, \"Verb:\", group.Verb)\n    fmt.Println(\"Seen:\", group.IsSeen, \"Read:\", group.IsRead)\n    fmt.Println(\"Activities:\", group.ActivityCount, \"Actors:\", group.ActorCount)\n    for _, activity := range group.Activities {\n        // ...\n    }\n}\n```\n\n\n#### Options\n\nYou can pass supported options and filters when retrieving activities:\n\n```go\nresp, err := flat.GetActivities(\n    ctx,\n    stream.WithActivitiesIDGTE(\"f505b3fb-a212-11e7-...\"),\n    stream.WithActivitiesLimit(5),\n    ...,\n)\n```\n\n### Adding activities\n\nAdd a single activity:\n\n```go\nresp, err := feed.AddActivity(ctx, stream.Activity{Actor: \"bob\", ...})\nif err != nil {\n    // ...\n}\n\nfmt.Println(\"Duration:\", resp.Duration)\nfmt.Println(\"Rate:\", resp.Rate)\nfmt.Println(\"Activity:\", resp.Activity) // resp wraps the stream.Activity type\n```\n\nAdd multiple activities:\n\n```go\na1 := stream.Activity{Actor: \"bob\", ...}\na2 := stream.Activity{Actor: \"john\", ...}\na3 := stream.Activity{Actor: \"alice\", ...}\n\nresp, err := feed.AddActivities(ctx, a1, a2, a3)\nif err != nil {\n    // ...\n}\n\nfmt.Println(\"Duration:\", resp.Duration)\nfmt.Println(\"Rate:\", resp.Rate)\nfmt.Println(\"Activities:\")\nfor _, activity := range resp.Activities {\n    fmt.Println(activity)\n}\n```\n\n### Updating activities\n\n```go\n_, err := feed.UpdateActivities(ctx, a1, a2, ...)\nif err != nil {\n    // ...\n}\n```\n\n### Partially updating activities\n\nYou can partial update activities identified either by ID:\n\n``` go\nchangesetA := stream.NewUpdateActivityRequestByID(\"f505b3fb-a212-11e7-...\", map[string]any{\"key\": \"new-value\"}, []string{\"removed\", \"keys\"})\nchangesetB := stream.NewUpdateActivityRequestByID(\"f707b3fb-a212-11e7-...\", map[string]any{\"key\": \"new-value\"}, []string{\"removed\", \"keys\"})\nresp, err := client.PartialUpdateActivities(ctx, changesetA, changesetB)\nif err != nil {\n    // ...\n}\n```\n\nor by a ForeignID and timestamp pair:\n``` go\nchangesetA := stream.NewUpdateActivityRequestByForeignID(\"dothings:1\", stream.Time{...}, map[string]any{\"key\": \"new-value\"}, []string{\"removed\", \"keys\"})\nchangesetB := stream.NewUpdateActivityRequestByForeignID(\"dothings:2\", stream.Time{...}, map[string]any{\"key\": \"new-value\"}, []string{\"removed\", \"keys\"})\nresp, err := client.PartialUpdateActivities(ctx, changesetA, changesetB)\nif err != nil {\n    // ...\n}\n```\n\n### Removing activities\n\nYou can either remove activities by ID or ForeignID:\n\n```go\n_, err := feed.RemoveActivityByID(ctx, \"f505b3fb-a212-11e7-...\")\nif err != nil {\n    // ...\n}\n\n_, err := feed.RemoveActivityByForeignID(ctx, \"bob:123\")\nif err != nil {\n    // ...\n}\n```\n\n### Following another feed\n\n```go\n_, err := feed.Follow(ctx, anotherFeed)\nif err != nil {\n    // ...\n}\n```\n\nBeware that it's possible to follow only flat feeds.\n\n#### Options\n\nYou can pass options to the `Follow` method. For example:\n\n```go\n_, err := feed.Follow(ctx,\n    anotherFeed,\n    stream.WithFollowFeedActivityCopyLimit(15),\n    ...,\n)\n```\n\n### Retrieving followers and followings\n\n#### Following\n\nGet the feeds that a feed is following:\n\n```go\nresp, err := feed.GetFollowing(ctx)\nif err != nil {\n    // ...\n}\n\nfmt.Println(\"Duration:\", resp.Duration)\nfor _, followed := range resp.Results {\n    fmt.Println(followed.FeedID, followed.TargetID)\n}\n```\n\nYou can pass options to `GetFollowing`:\n\n```go\nresp, err := feed.GetFollowing(\n    ctx,\n    stream.WithFollowingLimit(5),\n    ...,\n)\n```\n\n#### Followers\n\n```go\nresp, err := flat.GetFollowers(ctx)\nif err != nil {\n    // ...\n}\n\nfmt.Println(\"Duration:\", resp.Duration)\nfmt.Println(\"Rate:\", resp.Rate)\nfor _, follower := range resp.Results {\n    fmt.Println(follower.FeedID, follower.TargetID)\n}\n```\n\nNote: this is only possible for `FlatFeed` types.\n\nYou can pass options to `GetFollowers`:\n\n```go\nresp, err := feed.GetFollowing(\n    ctx,\n    stream.WithFollowersLimit(5),\n    ...,\n)\n```\n\n### Unfollowing a feed\n\n```go\n_, err := flat.Unfollow(ctx, anotherFeed)\nif err != nil {\n    // ...\n}\n```\n\nYou can pass options to `Unfollow`:\n\n```go\n_, err := flat.Unfollow(ctx,\n    anotherFeed,\n    stream.WithUnfollowKeepHistory(true),\n    ...,\n)\n```\n\n### Updating an activity's `to` targets\n\nRemove all old targets and set new ones (replace):\n\n```go\nnewTargets := []stream.Feed{f1, f2}\n\n_, err := feed.UpdateToTargets(ctx, activity, stream.WithToTargetsNew(newTargets...))\nif err != nil {\n    // ...\n}\n```\n\nAdd some targets and remove some others:\n\n```go\nadd := []stream.Feed{target1, target2}\nremove := []stream.Feed{oldTarget1, oldTarget2}\n\n_, err := feed.UpdateToTargets(\n    ctx,\n    activity,\n    stream.WithToTargetsAdd(add),\n    stream.WithToTargetsRemove(remove),\n)\nif err != nil {\n    // ...\n}\n```\n\nNote: you can't mix `stream.WithToTargetsNew` with `stream.WithToTargetsAdd` or `stream.WithToTargetsRemove`.\n\n### Batch adding activities\n\nYou can add the same activities to multiple feeds at once with the `(*Client).AddToMany` method ([docs](https://getstream.io/docs_rest/#add_to_many)):\n\n```go\n_, err := client.AddToMany(ctx,\n    activity, feed1, feed2, ...,\n)\nif err != nil {\n    // ...\n}\n```\n\n### Batch creating follows\n\nYou can create multiple follow relationships at once with the `(*Client).FollowMany` method ([docs](https://getstream.io/docs_rest/#follow_many)):\n\n```go\nrelationships := []stream.FollowRelationship{\n    stream.NewFollowRelationship(source, target),\n    ...,\n}\n\n_, err := client.FollowMany(ctx, relationships)\nif err != nil {\n    // ...\n}\n```\n\n### Realtime tokens\n\nYou can get a token suitable for client-side [real-time feed updates](https://getstream.io/docs/go/#realtime) as:\n\n```go\n// Read+Write token\ntoken := feed.RealtimeToken(false)\n\n// Read-only token\nreadonlyToken := feed.RealtimeToken(true)\n```\n\n## Analytics\n\nIf your app is enabled for analytics collection you can use the Go client to track events. The main documentation for the analytics features is available [in our Docs page](https://getstream.io/docs/#analytics_setup).\n\n### Obtaining an Analytics client\n\nYou can obtain a specialized Analytics client (`*stream.AnalyticsClient`) from a regular client, which you can use to track events:\n\n```go\n// Create the client\nanalytics := client.Analytics()\n```\n\n### Tracking engagement\n\nEngagement events can be tracked with the `TrackEngagement` method of `AnalyticsClient`. It accepts any number of `EngagementEvent`s.\n\nEvents' syntax is not checked by the client, so be sure to follow our [documentation](https://getstream.io/docs/#analytics_engagements) about it.\n\nEvents are simple maps, but the `stream` package offers handy helpers to populate such events easily.\n\n```go\n// Create the event\nevent := stream.EngagementEvent{}.\n    WithLabel(\"click\").\n    WithForeignID(\"event:1234\").\n    WithUserData(stream.NewUserData().String(\"john\")).\n    WithFeatures(\n        stream.NewEventFeature(\"color\", \"blue\"),\n        stream.NewEventFeature(\"shape\", \"rectangle\"),\n    ).\n    WithLocation(\"homepage\")\n\n// Track the event(s)\n_, err := analytics.TrackEngagement(ctx, event)\nif err != nil {\n    // ...\n}\n```\n\n### Tracking impressions\n\nImpression events can be tracked with the `TrackImpression` method of `AnalyticsClient` ([syntax docs](https://getstream.io/docs/#analytics_impressions)):\n\n```go\n// Create the impression events\nimp := stream.ImpressionEventData{}.\n    WithForeignIDs(\"product:1\", \"product:2\", \"product:3\").\n    WithUserData(stream.NewUserData().String(\"john\")).\n    WithLocation(\"storepage\")\n\n// Track the events\n_, err := analytics.TrackImpression(ctx, imp)\nif err != nil {\n    // ...\n}\n```\n\n### Email tracking\n\nYou can generate URLs to track events and redirect to a specific URL with the `RedirectAndTrack` method of `AnalyticsClient` ([syntax docs](https://getstream.io/docs/#analytics_email)). It accepts any number of engagement and impression events:\n\n```go\n// Create the events\nengagement := stream.EngagementEvent{}.\n    WithLabel(\"click\").\n    WithForeignID(\"event:1234\").\n    WithUserData(stream.NewUserData().String(\"john\")).\n    WithFeatures(\n        stream.NewEventFeature(\"color\", \"blue\"),\n        stream.NewEventFeature(\"shape\", \"rectangle\"),\n    ).\n    WithLocation(\"homepage\")\n\nimpressions := stream.ImpressionEventData{}.\n    WithForeignIDs(\"product:1\", \"product:2\", \"product:3\").\n    WithUserData(stream.NewUserData().String(\"john\")).\n    WithLocation(\"storepage\")\n\n// Generate the tracking and redirect URL, which once followed\n// will redirect the user to the targetURL.\ntargetURL := \"https://google.com\"\nurl, err := analytics.RedirectAndTrack(targetURL, engagement, impression)\nif err != nil {\n    // ...\n}\n\n// Display the obtained url where needed.\n```\n\n## Personalization\n\n[Personalization endpoints](https://getstream.io/personalization) for enabled apps can be reached using a `PersonalizationClient`, a specialized client obtained with the `Personalization()` function of a regular `Client`.\n\n```go\npersonalization := client.Personalization()\n```\n\nThe `PersonalizationClient` exposes three functions that you can use to retrieve and manipulate data: `Get`, `Post`, and `Delete`.\n\nFor example, to retrieve follow recommendations:\n\n```go\n// Get follow recommendations\ndata := map[string]any{\n    \"user_id\":          123,\n    \"source_feed_slug\": \"timeline\",\n    \"target_feed_slug\": \"user\",\n}\nresp, err = personalization.Get(ctx, \"follow_recommendations\", data)\nif err != nil {\n    // ...\n}\nfmt.Println(resp)\n```\n\nSee the complete [docs and examples](https://getstream.io/docs/#personalization_introduction) about personalization features on Stream's documentation pages.\n\n## Collections\n\n[Collections](https://getstream.io/docs/#collections_introduction) endpoints can be reached using a specialized `CollectionsClient` which, like `PersonalizationClient`, can be obtained from a regular `Client`:\n\n```go\ncollections := client.Collections()\n```\n\n`CollectionsClient` exposes three batch functions, `Upsert`, `Select`, and `DeleteMany` as well as CRUD functions: `Add`, `Get`, `Update`, `Delete`:\n\n```go\n// Upsert the \"picture\" collection\nobject := stream.CollectionObject{\n    ID:   \"123\",\n    Data: map[string]any{\n        \"name\": \"Rocky Mountains\",\n        \"location\": \"North America\",\n    },\n}\n_, err = collections.Upsert(ctx, \"picture\", object)\nif err != nil {\n    // ...\n}\n\n// Get the data from the \"picture\" collection for ID \"123\" and \"456\"\nobjects, err := collections.Select(ctx, \"picture\", \"123\", \"456\")\nif err != nil {\n    // ...\n}\n\n// Delete the data from the \"picture\" collection for picture with ID \"123\"\n_, err = collections.Delete(ctx, \"picture\", \"123\")\nif err != nil {\n    // ...\n}\n\n// Get a single collection object from the \"pictures\" collection with ID \"123\"\n_, err = collections.Get(ctx, \"pictures\", \"123\")\nif err != nil {\n    // ...\n}\n```\n\nSee the complete [docs and examples](https://getstream.io/docs/#collections_introduction) about collections on Stream's documentation pages.\n\n## Users\n\n[Users](https://getstream.io/docs/#users_introduction) endpoints can be reached using a specialized `UsersClient` which, like `CollectionsClient`, can be obtained from a regular `Client`:\n\n```go\nusers := client.Users()\n```\n\n`UsersClient` exposes CRUD functions: `Add`, `Get`, `Update`, `Delete`:\n\n```go\nuser := stream.User{\n    ID: \"123\",\n    Data: map[string]any{\n        \"name\": \"Bobby Tables\",\n    },\n}\n\ninsertedUser, err := users.Add(ctx, user, false)\nif err != nil {\n    // ...\n}\n\nnewUserData :=map[string]any{\n    \"name\": \"Bobby Tables\",\n    \"age\": 7,\n}\n\nupdatedUser, err := users.Update(ctx, \"123\", newUserData)\nif err != nil {\n    // ...\n}\n\n_, err = users.Delete(ctx, \"123\")\nif err != nil {\n    // ...\n}\n```\n\nSee the complete [docs and examples](https://getstream.io/docs/#users_introduction) about users on Stream's documentation pages.\n\n## Reactions\n\n[Reactions](https://getstream.io/docs/#reactions_introduction) endpoints can be reached using a specialized `Reactions` which, like `CollectionsClient`, can be obtained from a regular `Client`:\n\n```go\nreactions := client.Reactions()\n```\n\n`Reactions` exposes CRUD functions: `Add`, `Get`, `Update`, `Delete`, as well as two specialized functions `AddChild` and `Filter`:\n\n```go\nr := stream.AddReactionRequestObject{\n    Kind: \"comment\",\n    UserID: \"123\",\n    ActivityID: \"87a9eec0-fd5f-11e8-8080-80013fed2f5b\",\n    Data: map[string]any{\n        \"text\": \"Nice post!!\",\n    },\n    TargetFeeds: []string{\"user:bob\", \"timeline:alice\"},\n}\n\ncomment, err := reactions.Add(ctx, r)\nif err != nil {\n    // ...\n}\n\nlike := stream.AddReactionRequestObject{\n    Kind: \"like\",\n    UserID: \"456\",\n}\n\nchildReaction, err := reactions.AddChild(ctx, comment.ID, like)\nif err != nil {\n    // ...\n}\n\n// If we fetch the \"comment\" reaction now, it will have the child reaction(s) present.\nparent, err := reactions.Get(ctx, comment.ID)\nif err != nil {\n    // ...\n}\n\nfor kind, children := range parent.ChildrenReactions {\n    // child reactions are grouped by kind\n}\n\n// update the target feeds for the `comment` reaction\nupdatedReaction, err := reactions.Update(ctx, comment.ID, nil, []string{\"timeline:jeff\"})\nif err != nil {\n    // ...\n}\n\n// get all reactions for the activity \"87a9eec0-fd5f-11e8-8080-80013fed2f5b\",\n// paginated 5 at a time, including the activity data\nresponse, err := reactions.Filter(ctx,\n    stream.ByActivityID(\"87a9eec0-fd5f-11e8-8080-80013fed2f5b\"),\n    stream.WithLimit(5),\n    stream.WithActivityData())\nif err != nil {\n    // ...\n}\n\n// since we requested the activity, it will be present in the response\nfmt.Println(response.Activity)\n\nfor _, reaction := range response.Results{\n    // do something for each reaction\n}\n\n//get the next page of reactions\nresponse, err = reactions.GetNextPageFilteredReactions(ctx, response)\nif err != nil {\n    // ...\n}\n\n// get all likes by user \"123\"\nresponse, err = reactions.Filter(ctx, stream.ByUserID(\"123\").ByKind(\"like\"))\nif err != nil {\n    // ...\n}\n```\n\nSee the complete [docs and examples](https://getstream.io/docs/#reactions_introduction) about reactions on Stream's documentation pages.\n\n## Enrichment\n\n[Enrichment](https://getstream.io/docs/#enrichment_introduction) is a way of retrieving activities from feeds in which references to `Users` and `Collections` will be replaced with the corresponding objects\n\n`FlatFeed`, `AggregatedFeed` and `NotificationFeed` each have a `GetEnrichedActivities` function to retrieve enriched activities.\n\n```go\nu := stream.User{\n    ID: \"123\",\n    Data: map[string]any{\n        \"name\": \"Bobby Tables\",\n    },\n}\n\n// We add a user\nuser, err := client.Users().Add(ctx, u, true)\nif err != nil {\n    // ...\n}\n\nc := stream.CollectionObject{\n    ID: \"123\",\n    Data: map[string]any{\n        \"name\":     \"Rocky Mountains\",\n        \"location\": \"North America\",\n    },\n}\n\n// We add a collection object\ncollectionObject, err := client.Collections().Add(ctx, \"picture\", c)\nif err != nil {\n    // ...\n}\n\nact := stream.Activity{\n    Time:      stream.Time{Time: time.Now()},\n    Actor:     client.Users().CreateReference(user.ID),\n    Verb:      \"post\",\n    Object:    client.Collections().CreateReference(\"picture\", collectionObject.ID),\n    ForeignID: \"picture:1\",\n}\n\n\n// We add the activity to the user's feed\nfeed, _ := client.FlatFeed(\"user\", \"123\")\n_, err = feed.AddActivity(ctx, act)\nif err != nil {\n    // ...\n}\n\nresult, err := feed.GetActivities(ctx)\nif err != nil {\n    // ...\n}\nfmt.Println(result.Results[0].Actor) // Will output the user reference\nfmt.Println(result.Results[0].Object) // Will output the collection reference\n\nenrichedResult, err := feed.GetEnrichedActivities(ctx)\nif err != nil {\n    // ...\n}\nfmt.Println(enrichedResult.Results[0][\"actor\"].(map[string]any)) // Will output the user object\nfmt.Println(enrichedResult.Results[0][\"object\"].(map[string]any)) // Will output the collection object\n```\n\nSee the complete [docs and examples](https://getstream.io/docs/#enrichment_introduction) about enrichment on Stream's documentation pages.\n\n## License\n\nProject is licensed under the [BSD 3-Clause](LICENSE).\n\n## We are hiring!\n\nWe've recently closed a [$38 million Series B funding round](https://techcrunch.com/2021/03/04/stream-raises-38m-as-its-chat-and-activity-feed-apis-power-communications-for-1b-users/) and we keep actively growing.\nOur APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world.\n\nCheck out our current openings and apply via [Stream's website](https://getstream.io/team/#jobs).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGetStream%2Fstream-go2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGetStream%2Fstream-go2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGetStream%2Fstream-go2/lists"}