feat(chat): migrate GET /api/artists/{id}/posts#1695
Conversation
Swap hardcoded api.recoupable.com/api/posts for getClientApiBaseUrl() with the artist id on the path, send Privy Bearer auth, and gate useArtistPosts on Privy authenticated so the queryFn does not fire before the session hydrates. Extract the Post type to types/Post.ts so consumers import directly (no re-export indirection) and update the inlined docs samples to the new path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis pull request consolidates the Changes
Sequence Diagram(s)sequenceDiagram
participant Client as React Component
participant Hook as useArtistPosts
participant Privy as Privy Auth
participant Fetch as fetchPosts
participant API as API Server
Client->>Hook: Call with artistAccountId
Hook->>Privy: Check authenticated status
alt Not Authenticated
Privy-->>Hook: authenticated = false
Hook-->>Client: Query disabled
else Authenticated
Privy-->>Hook: authenticated = true
Hook->>Privy: getAccessToken()
Privy-->>Hook: accessToken
Hook->>Fetch: fetchPosts({ artistAccountId, page, limit, accessToken })
Fetch->>API: GET /api/artists/{artistAccountId}/posts<br/>Header: Authorization: Bearer {accessToken}
API-->>Fetch: Posts data
Fetch-->>Hook: PostsResponse
Hook-->>Client: InfiniteData<PostsResponse>
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 64f83190ec
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| curl: `curl -X GET "https://recoup-api.vercel.app/api/artists/YOUR_ARTIST_ACCOUNT_ID/posts?page=1&limit=20" \\ | ||
| -H "Content-Type: application/json"`, |
There was a problem hiding this comment.
Add bearer auth to posts API examples
After switching the docs to GET /api/artists/{id}/posts, the request snippets still show only Content-Type headers, but the client implementation now always sends Authorization: Bearer ... in lib/recoup/fetchPosts.ts. Anyone copying these examples as-is will hit 401/unauthorized against the new endpoint, so the migration leaves the documentation examples functionally incorrect.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
2 issues found across 8 files
Confidence score: 4/5
- This PR looks safe to merge with minimal runtime risk, since the reported issues are documentation-focused rather than behavior changes in production code.
- The most significant issue is in
app/docs/posts/constants.ts:/api/artists/{id}/postsexamples missing the Bearer auth header can mislead integrators and cause unauthorized request failures when using Privy access tokens. app/docs/posts/page.tsxhas inconsistent parameter naming (artistAccountIdvsartist_account_id), which can create avoidable client confusion but is low severity.- Pay close attention to
app/docs/posts/constants.tsandapp/docs/posts/page.tsx- align auth and parameter docs to prevent integration mistakes.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/docs/posts/page.tsx">
<violation number="1" location="app/docs/posts/page.tsx:32">
P3: Parameter naming is inconsistent in the docs: the endpoint path uses `artistAccountId` while the documented parameter is `artist_account_id`. Use one canonical name to avoid client confusion.</violation>
</file>
<file name="app/docs/posts/constants.ts">
<violation number="1" location="app/docs/posts/constants.ts:15">
P2: The updated `/api/artists/{id}/posts` examples omit the Bearer auth header. Since this route is now called with a Privy access token, these docs examples can lead users to unauthorized request failures.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| } | ||
|
|
||
| response = requests.get("https://api.recoupable.com/api/posts", headers=headers, params=params) | ||
| response = requests.get("https://recoup-api.vercel.app/api/artists/YOUR_ARTIST_ACCOUNT_ID/posts", headers=headers, params=params) |
There was a problem hiding this comment.
P2: The updated /api/artists/{id}/posts examples omit the Bearer auth header. Since this route is now called with a Privy access token, these docs examples can lead users to unauthorized request failures.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/docs/posts/constants.ts, line 15:
<comment>The updated `/api/artists/{id}/posts` examples omit the Bearer auth header. Since this route is now called with a Privy access token, these docs examples can lead users to unauthorized request failures.</comment>
<file context>
@@ -8,14 +8,13 @@ headers = {
}
-response = requests.get("https://api.recoupable.com/api/posts", headers=headers, params=params)
+response = requests.get("https://recoup-api.vercel.app/api/artists/YOUR_ARTIST_ACCOUNT_ID/posts", headers=headers, params=params)
data = response.json()`,
- javascript: `fetch("https://api.recoupable.com/api/posts?artist_account_id=YOUR_ARTIST_ACCOUNT_ID&page=1&limit=20", {
</file context>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@hooks/useArtistPosts.ts`:
- Around line 24-45: The query cache key for useInfiniteQuery in
useArtistPosts.ts is missing the authenticated user's identifier, causing cached
posts to leak between users; update the queryKey from ["posts", artistAccountId,
limit] to include the current user's account id (the same user identifier used
elsewhere, e.g. userData?.account_id) so it becomes ["posts",
userData?.account_id, artistAccountId, limit]; obtain userData/account_id from
the same auth/context hook used in
useProStatus/useCredits/useAccountOrganizations (or from usePrivy if it exposes
the user id) and add the same change to useArtistFans.ts at the analogous
queryKey to keep cache boundaries per-user.
In `@lib/recoup/fetchPosts.ts`:
- Around line 34-36: The URL construction in fetchPosts.ts currently
interpolates artistAccountId and query params directly; update the const url
creation to encode the path segment and set queries via URL.searchParams: use
encodeURIComponent(artistAccountId) when building the path from
getClientApiBaseUrl(), then create new URL(...) for the base path and call
url.searchParams.set('page', String(page)) and url.searchParams.set('limit',
String(limit)) so reserved characters in artistAccountId and query values are
safely encoded (identify and change the const url, getClientApiBaseUrl(),
artistAccountId, page, and limit usages).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 45c42598-1d1e-48bf-bd20-96a1b1f56101
⛔ Files ignored due to path filters (1)
types/Post.tsis excluded by none and included by none
📒 Files selected for processing (5)
components/Posts/PostCard.tsxcomponents/Posts/Posts.tsxcomponents/Posts/PostsWrapper.tsxhooks/useArtistPosts.tslib/recoup/fetchPosts.ts
| const { getAccessToken, authenticated } = usePrivy(); | ||
|
|
||
| return useInfiniteQuery<PostsResponse, PostsError>({ | ||
| queryKey: ["posts", artistAccountId, limit], | ||
| queryFn: ({ pageParam }) => | ||
| fetchPosts({ | ||
| queryFn: async ({ pageParam }) => { | ||
| const accessToken = await getAccessToken(); | ||
| if (!accessToken) { | ||
| throw { message: "No access token" } as PostsError; | ||
| } | ||
| return fetchPosts({ | ||
| artistAccountId: artistAccountId!, | ||
| accessToken, | ||
| page: pageParam as number, | ||
| limit, | ||
| }), | ||
| }); | ||
| }, | ||
| getNextPageParam: (lastPage: PostsResponse) => { | ||
| const { page, total_pages } = lastPage.pagination; | ||
| return page < total_pages ? page + 1 : undefined; | ||
| }, | ||
| initialPageParam: 1, | ||
| enabled: !!artistAccountId, | ||
| enabled: !!artistAccountId && authenticated, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect how auth-dependent React Query keys are scoped elsewhere.
rg -n -C3 --type=ts --type=tsx 'queryKey:\s*\['
rg -n -C3 --type=ts --type=tsx 'const\s*\{[^}]*user[^}]*\}\s*=\s*usePrivy\('
rg -n -C3 --type=ts --type=tsx 'removeQueries|invalidateQueries|clear\('Repository: recoupable/chat
Length of output: 142
🏁 Script executed:
#!/bin/bash
# Re-run with corrected rg type flags (ts only, not tsx)
# and examine the actual file
echo "=== Examining hooks/useArtistPosts.ts ==="
cat -n hooks/useArtistPosts.ts
echo -e "\n=== Searching for queryKey patterns in .ts/.tsx files ==="
rg -n 'queryKey:\s*\[' --type ts
echo -e "\n=== Searching for usePrivy usage ==="
rg -n 'usePrivy' --type ts -A 3
echo -e "\n=== Searching for query invalidation/clearing ==="
rg -n 'invalidateQueries|removeQueries|resetQueries' --type ts -A 2Repository: recoupable/chat
Length of output: 28612
Scope query cache keys with authenticated user context.
The query cache key ["posts", artistAccountId, limit] doesn't account for the authenticated user. When a user logs out and another logs in, the same artist's cached posts can be served to a different user. Add a user identifier to the cache boundary.
The codebase already uses this pattern elsewhere (e.g., useProStatus, useCredits, useAccountOrganizations all include userData?.account_id). Apply the same approach here, and note that useArtistFans.ts has an identical issue at line 24.
Suggested fix
- const { getAccessToken, authenticated } = usePrivy();
+ const { getAccessToken, authenticated } = usePrivy();
+ const { userData } = useUserProvider();
return useInfiniteQuery<PostsResponse, PostsError>({
- queryKey: ["posts", artistAccountId, limit],
+ queryKey: ["posts", userData?.account_id, artistAccountId, limit],
...
- enabled: !!artistAccountId && authenticated,
+ enabled: !!artistAccountId && authenticated && !!userData?.account_id,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/useArtistPosts.ts` around lines 24 - 45, The query cache key for
useInfiniteQuery in useArtistPosts.ts is missing the authenticated user's
identifier, causing cached posts to leak between users; update the queryKey from
["posts", artistAccountId, limit] to include the current user's account id (the
same user identifier used elsewhere, e.g. userData?.account_id) so it becomes
["posts", userData?.account_id, artistAccountId, limit]; obtain
userData/account_id from the same auth/context hook used in
useProStatus/useCredits/useAccountOrganizations (or from usePrivy if it exposes
the user id) and add the same change to useArtistFans.ts at the analogous
queryKey to keep cache boundaries per-user.
| const url = new URL( | ||
| `${getClientApiBaseUrl()}/api/artists/${artistAccountId}/posts?page=${page}&limit=${limit}`, | ||
| ); |
There was a problem hiding this comment.
Encode the artist path segment and query params.
artistAccountId is now part of the path, so reserved URL characters like /, ?, #, or % can alter the route. Build the path with encodeURIComponent() and set query params through URL.searchParams.
Proposed fix
- const url = new URL(
- `${getClientApiBaseUrl()}/api/artists/${artistAccountId}/posts?page=${page}&limit=${limit}`,
- );
+ const url = new URL(
+ `${getClientApiBaseUrl()}/api/artists/${encodeURIComponent(
+ artistAccountId,
+ )}/posts`,
+ );
+ url.searchParams.set("page", String(page));
+ url.searchParams.set("limit", String(limit));As per coding guidelines, “Implement built-in security practices for authentication and data handling.”
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/recoup/fetchPosts.ts` around lines 34 - 36, The URL construction in
fetchPosts.ts currently interpolates artistAccountId and query params directly;
update the const url creation to encode the path segment and set queries via
URL.searchParams: use encodeURIComponent(artistAccountId) when building the path
from getClientApiBaseUrl(), then create new URL(...) for the base path and call
url.searchParams.set('page', String(page)) and url.searchParams.set('limit',
String(limit)) so reserved characters in artistAccountId and query values are
safely encoded (identify and change the const url, getClientApiBaseUrl(),
artistAccountId, page, and limit usages).
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/docs/posts/constants.ts">
<violation number="1">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**
PR-stated migration target and updated API examples are inconsistent: code now documents `/api/posts?artist_account_id=...` instead of `/api/artists/{id}/posts`, creating a fabricated/misaligned change signal.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
E2E integration verified against api PR #469 previewLoaded this chat preview with Flow:
Kanye West account has 0 posts in the preview DB, but the plumbing — override, auth, routing, response shape — all work. |
platform is derived client-side from post_url via getPlatform() in
PostCard; it's never read off the API response. The new
/api/artists/{id}/posts response doesn't include it either, so the
optional field is dead weight.
Browser smoke testAgainst preview Flow: Logged in → navigated to Request 1 — empty case (Justin Bieber, 0 posts)Response body: { "status": "success", "posts": [], "pagination": { "total_count": 0, "page": 1, "limit": 20, "total_pages": 1 } }Request 2 — populated case (PinkPantheress, 16 posts)Response body (trimmed): {
"status": "success",
"posts": [
{ "id": "ddebb5b1-a60c-4a54-8bdd-27e486f7355e", "post_url": "https://www.instagram.com/p/DOOiLqHDlps/", "updated_at": "2025-09-05T16:22:16+00:00" },
{ "id": "16420a97-4622-490e-883f-b6d7e1c77815", "post_url": "https://www.instagram.com/p/DNTSuzkubQP/", "updated_at": "2025-08-13T16:12:04+00:00" },
...14 more...
],
"pagination": { "total_count": 16, "page": 1, "limit": 20, "total_pages": 1 }
}Request headers on both calls
Verified
End-to-end chain (chat UI → Privy Bearer → migrated endpoint → Supabase → Instagram embed) confirmed working. 🤖 Tested with Claude Code via Chrome DevTools MCP |
Summary
Migrates
fetchPosts.tsanduseArtistPoststo the new/api/artists/{id}/postsendpoint.Test plan
Summary by CodeRabbit
New Features
Refactor