Skip to content

docs(swagger): rename notifications path placeholder user_id -> id#838

Merged
raymondjacobson merged 1 commit into
mainfrom
api/notifications-path-id-rename
May 20, 2026
Merged

docs(swagger): rename notifications path placeholder user_id -> id#838
raymondjacobson merged 1 commit into
mainfrom
api/notifications-path-id-rename

Conversation

@raymondjacobson
Copy link
Copy Markdown
Member

@raymondjacobson raymondjacobson commented 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:

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) after this merges.

Test plan

  • Spec validates in CI.
  • Hitting /notifications/<hashed-id>?user_id=<hashed-id> post-deploy returns the same response as before.
  • Regenerating the apps SDK against the merged spec produces a clean GetNotificationsRequest with id (path) and userId (query), no userId2.

🤖 Generated with Claude Code

Aligns /notifications/{user_id} and /notifications/{user_id}/playlist_updates
with the convention used by every /users/{id}/... route. The concrete URL on
the wire is unchanged (Fiber routes /notifications/:userId by position, not
placeholder name), so no Go change is required.

Motivation: a follow-up to #837, which added a user_id query parameter to
both routes for personalization. With the path placeholder also named
user_id, openapi-generator emitted a colliding TS field (userId2) in the
generated SDK request type. Renaming the path placeholder to id avoids the
collision and gives the SDK a clean, conventional shape:

  GetNotificationsRequest { id: string; userId?: string; ... }

This is a breaking change for external @audius/sdk consumers of the two
generated methods (the path field renames from userId to id). Wire
behaviour, server behaviour, and request URLs are unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@raymondjacobson raymondjacobson merged commit 4e763a9 into main May 20, 2026
5 checks passed
@raymondjacobson raymondjacobson deleted the api/notifications-path-id-rename branch May 20, 2026 20:24
raymondjacobson added a commit to AudiusProject/apps that referenced this pull request May 20, 2026
Picks up AudiusProject/api#838, which renamed the OpenAPI path placeholder
on /notifications/{user_id} and /notifications/{user_id}/playlist_updates
to /notifications/{id} and /notifications/{id}/playlist_updates. With the
collision against the query param gone, the generator now emits clean
shapes:

  GetNotificationsRequest    { id: string; userId?: string; ... }
  GetPlaylistUpdatesRequest  { id: string; userId?: string }

Update the three call sites to use { id, userId } instead of the previous
awkward { userId, userId2 }. Concrete URL is unchanged.

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>
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant