docs(swagger): add user_id query param to notifications endpoints#837
Merged
Conversation
The /notifications/{user_id} and /notifications/{user_id}/playlist_updates
endpoints currently take the notifications owner via the URL path only. The
backend computes personalization (e.g. does_current_user_follow on embedded
related.users) from resolveMyIdMiddleware, which reads c.Query("user_id") —
not the path. With no query value, MyID is 0 and every embedded user comes
back with does_current_user_follow: false, polluting clients that prime
related users into a shared cache.
Declare an optional user_id query parameter on both endpoints so SDK
consumers can send the requester id explicitly. This is kept separate from
the path user_id because a manager may read a managed user's notifications,
in which case the requester and the owner are different users.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced May 20, 2026
raymondjacobson
added a commit
that referenced
this pull request
May 20, 2026
) ## Summary Rename the OpenAPI path placeholder on the two notifications routes from `{user_id}` to `{id}`, matching the convention used by every `/users/{id}/...` route. Concrete URL is unchanged. - `/notifications/{user_id}` → `/notifications/{id}` - `/notifications/{user_id}/playlist_updates` → `/notifications/{id}/playlist_updates` ## Why Follow-up to #837, which added a `user_id` query parameter to both routes for personalization of embedded `related.users`. With the path placeholder *also* named `user_id`, `openapi-generator` (typescript-fetch) emitted a colliding TS field (`userId2`) in the generated `GetNotificationsRequest` / `GetPlaylistUpdatesRequest`. The wire format was correct but the public SDK type surface was awkward. Renaming the path placeholder to `id` lets the generator produce a clean shape: ```ts GetNotificationsRequest { id: string; userId?: string; ... } ``` The Fiber router matches `/notifications/:userId` by position, not by name, so no Go change is required and the concrete URL clients send is unchanged. ## Breaking change This **is** a breaking change for external `@audius/sdk` consumers of `getNotifications` / `getPlaylistUpdates`: the path field renames from `userId` to `id`. Server behaviour, wire format, and URL paths are unchanged. In-monorepo call sites will be updated in the corresponding apps PR ([AudiusProject/apps#14366](AudiusProject/apps#14366)) after this merges. ## Test plan - [x] Spec validates in CI. - [x] Hitting `/notifications/<hashed-id>?user_id=<hashed-id>` post-deploy returns the same response as before. - [x] Regenerating the apps SDK against the merged spec produces a clean `GetNotificationsRequest` with `id` (path) and `userId` (query), no `userId2`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
raymondjacobson
added a commit
to AudiusProject/apps
that referenced
this pull request
May 20, 2026
…14366) ## Summary - Regenerate `@audius/sdk` to pick up the new `user_id` query parameter and renamed path placeholder on `GET /notifications/{id}` and `GET /notifications/{id}/playlist_updates` (api PRs [AudiusProject/api#837](AudiusProject/api#837) + [AudiusProject/api#838](AudiusProject/api#838), both merged). - Pass the current user id through every call site so the backend personalizes embedded `related.users` in the response (`does_current_user_follow`, etc.). ## Why The notifications endpoint embeds user objects in `related.users`. The backend personalizes those via `MyID: app.getMyId(c)` in [`api/v1_notifications.go:336`](https://github.com/AudiusProject/api/blob/main/api/v1_notifications.go#L336), and [`getMyId`](https://github.com/AudiusProject/api/blob/main/api/resolve_middleware.go#L30) reads from the **query string** `?user_id=`, not the URL path. The SDK only put the id in the path, so on the backend `MyID = 0` and the SQL [short-circuited](https://github.com/AudiusProject/api/blob/main/api/dbv1/get_users.sql.go#L129-L136) `does_current_user_follow` to `false` for every embedded user. On a cold app boot, `useNotifications` fires early and `primeRelatedData` writes those un-personalized users into the tan-query cache. `primeUserData` only writes when the slot is empty (no `forceReplace`), so the bad entries stick until the user navigates to that profile and a personalized `getUser` fetch overwrites the cache. That's why visiting `@audius` directly and refreshing makes the Following state appear correct. A backend-only fix would collapse two semantically distinct ids (notifications owner vs. requester) — incorrect when a manager reads a managed user's notifications. The fix is client-side: send the requester id as `?user_id=`, keep the path id for whose notifications. ## Changes | File | Change | | --- | --- | | `packages/sdk/src/sdk/api/generated/default/apis/NotificationsApi.ts` | Regenerated. Path field renames `userId` → `id`; new `userId?: string` query field carries the requester id. | | `packages/sdk/src/sdk/api/generated/default/apis/UsersApi.ts` | Incidental drift: `getUserForYouFeed` was removed upstream and the generated copy was stale. | | `packages/common/src/api/tan-query/notifications/useNotifications.ts` | Pass `{ id, userId }` to the SDK. | | `packages/common/src/api/tan-query/notifications/useNotificationUnreadCount.ts` | Update local cast type and call site to use `id` as the path field. (No requester id passed here — the unread count doesn't hydrate `related.users`.) | | `packages/common/src/api/tan-query/playlist-updates/usePlaylistUpdates.ts` | Pass `{ id, userId }` to the SDK; widen the local cast type. | ## Breaking SDK change (external consumers) The path field on `GetNotificationsRequest` and `GetPlaylistUpdatesRequest` renames from `userId` to `id`. External `@audius/sdk` consumers of these two methods will need to rename the field. Wire format and server behaviour are unchanged. ## Test plan - [ ] CI green (typecheck/lint/tests). - [ ] On a fresh app load with a signed-in account that follows `@audius`: confirm Following state renders correctly on lists hydrated from notification responses (e.g. notification panel actor avatars, recommended profiles surfaced from related users) without first having to navigate to `@audius`'s profile. - [ ] Network inspector on the notifications request shows `?user_id=<hashed-current-user-id>` and `does_current_user_follow: true` for users actually followed in `related.users`. - [ ] Manager flow: read notifications while acting as a managed user. `?user_id=` carries whatever the app treats as "current user" (the managed user in normal manager-switched state); personalization in the response reflects that perspective. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
3 tasks
raymondjacobson
added a commit
that referenced
this pull request
May 20, 2026
## Summary Adds an optional `user_id` query parameter to three routes that embed user objects in their response and rely on `app.getMyId(c)` to personalize them. Today the SDK request types don't expose this field, so clients never send `?user_id=` and the backend computes personalization against `MyID = 0` — i.e. `does_current_user_follow` and friends return false for every embedded user. - `GET /events/remix-contests` ([v1_events_remix_contests.go:187,195,215](https://github.com/AudiusProject/api/blob/main/api/v1_events_remix_contests.go#L187)) - `GET /users/{id}/contests` ([v1_users_contests.go:114,122,142](https://github.com/AudiusProject/api/blob/main/api/v1_users_contests.go#L114)) - `GET /playlists/new-releases` ([v1_playlists_new_releases.go:74](https://github.com/AudiusProject/api/blob/main/api/v1_playlists_new_releases.go#L74)) ## Why Audit follow-up to [#837](#837) / [#838](#838) (notifications). Same bug class: 1. Endpoint embeds users in `related.users` (or a collection's `user` field). 2. Backend handler passes `MyID: app.getMyId(c)` into `dbv1.ParallelParams` to personalize those users. 3. `getMyId(c)` reads from the query string `?user_id=`. With no query value, `MyID = 0` and the SQL [short-circuits](https://github.com/AudiusProject/api/blob/main/api/dbv1/get_users.sql.go#L129-L136) `does_current_user_follow` to false. 4. Web/mobile prime those un-personalized users into a shared tan-query cache (via `primeRelatedData` for remix-contests, `primeCollectionData` → `primeUserData` for new-releases). The cache write only happens when the slot is empty, so once poisoned the entry sticks until the user navigates to that profile and a personalized fetch overwrites it. Affected client hooks: - `useAllRemixContests` ([apps/packages/common/src/api/tan-query/events/useAllRemixContests.ts](https://github.com/AudiusProject/apps/blob/main/packages/common/src/api/tan-query/events/useAllRemixContests.ts)) - `useUserRemixContests` ([apps/packages/common/src/api/tan-query/events/useUserRemixContests.ts](https://github.com/AudiusProject/apps/blob/main/packages/common/src/api/tan-query/events/useUserRemixContests.ts)) - `useNewAlbumReleases` ([apps/packages/common/src/api/tan-query/collection/useNewAlbumReleases.ts](https://github.com/AudiusProject/apps/blob/main/packages/common/src/api/tan-query/collection/useNewAlbumReleases.ts)) ## Spec-only change `resolveMyIdMiddleware` already reads `c.Query(\"user_id\")` globally ([resolve_middleware.go:24-28](https://github.com/AudiusProject/api/blob/main/api/resolve_middleware.go#L24)), so the Go handlers personalize correctly as soon as the SDK starts sending the param. No backend code change required, no Go test updates required. No path-placeholder collision this time — all three routes are either query-only (`new-releases`, `remix-contests`) or already use `id` as the path placeholder (`users/{id}/contests`). The generator will emit a clean `userId?: string` field. ## Test plan - [ ] Spec validates in CI. - [ ] After SDK regen + apps update (follow-up PR): on a fresh app load with a signed-in account that follows artists surfaced on the Browse → New Albums and Explore → Contests pages, confirm Following state renders correctly without first having to visit each artist's profile. - [ ] Network inspector on the three endpoints shows `?user_id=<hashed-current-user-id>` and `does_current_user_follow: true` for users actually followed in `related.users`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
user_idquery parameter onGET /notifications/{user_id}andGET /notifications/{user_id}/playlist_updatesso SDK consumers can send the requester id alongside the notifications-owner path id.resolveMyIdMiddlewarealready readsc.Query("user_id")and feeds it togetMyId(c), so no backend code needs to change.Why
The notifications handler personalizes the embedded
related.usersfield viaMyID: app.getMyId(c)inapi/v1_notifications.go:336.getMyIdreads from the query string?user_id=, not from the path. Today the SDK only puts the user id in the path (/notifications/{user_id}), soMyIDresolves to0. Thedoes_current_user_followSQL short-circuits tofalse(WHERE $1 > 0 AND follower_user_id = $1inapi/dbv1/get_users.sql.go), and every embedded user returns withdoes_current_user_follow: false.Clients that prime these into a shared user cache (the web/mobile tan-query layer does this in
primeRelatedData) end up showing the current user as not-following accounts they actually follow — until they navigate to that user's profile directly and a personalized fetch overwrites the cache entry.A backend-only fix (
MyID: app.getUserId(c)) would collapse two semantically different ids: the notifications owner (path) and the requester (current user). They are intentionally separable, since a manager can read a managed user's notifications and should still see relationships from their own perspective. Carrying the requester id explicitly via the query keeps the two distinct.Notes for SDK regen
user_id. OpenAPI 3 permits same-name params in differentinlocations, butopenapi-generator-cli(typescript-fetch, v6.0.0) may emit a duplicateuserIdfield inGetNotificationsRequest. If regen fails or produces an ambiguous type, options: rename the path placeholder toid(concrete URL unchanged; breaking change for@audius/sdkconsumers) or usex-codegen-param-nameto disambiguate without altering the wire format.Test plan
swagger-cli validate/ equivalent in CI).does_current_user_followreturns true for users the current user actually follows inrelated.userson a fresh app boot (no profile navigation required).user_idin the query produces personalization from the manager's perspective.🤖 Generated with Claude Code