feat: Move GraphQL claims operations to backend API#626
Conversation
WalkthroughMigrates claim operations from Apollo/GraphQL to REST with TanStack Query hooks, centralizes auth token retrieval, adds DataCite GraphQL helper and Next.js GET/POST/DELETE routes, updates the Claim component to use new hooks, and adjusts Cypress GraphQL mocks. ChangesClaims REST and React Query Migration
Sequence DiagramssequenceDiagram
participant Component as Claim component
participant Hooks as useClaimQuery / useCreate/DeleteMutation
participant QueryClient
participant REST as REST Endpoints (/claims)
Component->>Hooks: useClaimQuery(doi)
Hooks->>REST: GET /claims?doi=...
REST-->>Hooks: { claim, ... }
Hooks->>QueryClient: set claimKeys.detail(doi)
QueryClient-->>Component: claim data
Component->>Hooks: createClaimMutation()
Hooks->>REST: POST /claims
REST-->>Hooks: { claim, ... }
Hooks->>QueryClient: updateClaimInCache()
Component->>Hooks: deleteClaimMutation()
Hooks->>REST: DELETE /claims/[id]
REST-->>Hooks: { claim, ... }
Hooks->>QueryClient: updateClaimInCache()
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
akita
|
||||||||||||||||||||||||||||
| Project |
akita
|
| Branch Review |
claims-function
|
| Run status |
|
| Run duration | 01m 41s |
| Commit |
|
| Committer | Joseph Rhoads |
| View all properties for this run ↗︎ | |
| Test results | |
|---|---|
|
|
0
|
|
|
1
|
|
|
0
|
|
|
0
|
|
|
45
|
| View all changes introduced in this branch ↗︎ | |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/utils/apolloClient/builder.ts (1)
23-26:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winOmit
authorizationheader when unauthenticated instead of sending an empty value.Line 25 currently sends
authorization: '', which can trigger stricter auth middleware differently than a missing header.Suggested fix
return { headers: { ...headers, - authorization: token ? `Bearer ${token}` : '' + ...(token ? { authorization: `Bearer ${token}` } : {}) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/utils/apolloClient/builder.ts` around lines 23 - 26, The headers object in src/utils/apolloClient/builder.ts is currently setting authorization to an empty string when token is falsy; change the headers construction so the authorization header is only added when token is present (use a conditional spread or conditional property addition around the authorization value based on token) so that when unauthenticated the header is omitted entirely instead of sending authorization: ''. Target the headers creation logic and the token variable in builder.ts to implement this conditional addition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/`(main)/claims/route.ts:
- Around line 57-67: The POST JSON parsing must guard against non-object values
(e.g., null) before destructuring: after awaiting request.json() in route.ts
assign to body, verify that typeof body === 'object' && body !== null (and
optionally Array.isArray(body) === false) and if that check fails return
NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 }); only then
destructure const { doi, sourceId } = body and continue — update the code around
the body variable and the destructuring to perform this shape validation to
avoid throwing when body is null or not an object.
In `@src/components/Claim/Claim.tsx`:
- Around line 45-63: The current onCreate and onDelete handlers only set
claimError from successful mutation payloads and ignore mutation failures; add
onError handlers to both createClaim and deleteClaim calls (in onCreate and
onDelete) that call setClaimError with a user-facing message derived from the
mutation error (e.g., error.message or a parsed error body) so network/server
failures surface in the existing error UI; preserve the existing onSuccess
behavior that reads result.claim?.errorMessages?.[0]?.title.
- Around line 37-38: The add/remove buttons allow double submissions because the
code only extracts mutate ({ mutate: createClaim } and { mutate: deleteClaim })
and doesn't guard in-flight; update the calls to also pull the mutation status
(e.g., { mutate: createClaim, isLoading: isCreating } =
useCreateClaimMutation(doi_id) and { mutate: deleteClaim, isLoading: isDeleting
} = useDeleteClaimMutation(doi_id)) or introduce a local boolean (e.g.,
isSubmitting) set true before calling mutate and false in onSuccess/onError,
then use the combined guard (isCreating || isDeleting || isSubmitting) to set
the buttons' disabled prop (also apply the same change for the other buttons
referenced around lines 99-119) so further clicks are ignored while a request is
in flight.
In `@src/data/queries/claimQuery.ts`:
- Around line 53-55: Replace usage of json.errors[0].message with the mutation
error title field when constructing thrown errors in claimQuery.ts; update the
two sites (the block at the shown if (json.errors?.length) and the similar block
around lines 70-72) to use json.errors[0].title (with a sensible fallback like
the existing 'Failed to create claim' or json.errors[0].message if present) so
the thrown Error includes the backend's title string.
In `@src/utils/auth.ts`:
- Around line 4-6: The session cookie JSON parsing in utils/auth.ts is unguarded
and can throw on malformed '_datacite' data; wrap the JSON.parse of ((cookies()
as unknown as UnsafeUnwrappedCookies).get('_datacite')?.value || '{}') in a
try/catch (or use a safeParse helper) so that parse errors are caught and the
code falls back to an empty object (or logs and returns an unauthenticated
result) instead of letting the request crash; update the reference variable
sessionCookie and any downstream logic to handle the fallback shape.
- Around line 1-5: The getAuthToken function currently uses the deprecated
UnsafeUnwrappedCookies and sync cookies() call; change getAuthToken to be async,
call and await cookies() (removing UnsafeUnwrappedCookies usage), parse the
'_datacite' cookie value safely and return the token (string | undefined), and
then update all callers of getAuthToken to await the function; reference
getAuthToken and the cookies() call to locate and update the code.
In `@src/utils/dataciteGraphql.ts`:
- Around line 95-99: The fetch to `${DATACITE_API_URL}/graphql` is made without
a timeout and can hang; wrap the POST fetch call with an AbortController and a
timer: create an AbortController, pass its signal to fetch, start a setTimeout
that calls controller.abort() after a configurable timeout (e.g., 5s), and clear
the timer on successful response; update the code around the fetch invocation
(the fetch call that builds response, the variables DATACITE_API_URL and
headers/body usage remain the same) and ensure aborted requests are
handled/translated into a clear error/timeout response instead of hanging.
---
Outside diff comments:
In `@src/utils/apolloClient/builder.ts`:
- Around line 23-26: The headers object in src/utils/apolloClient/builder.ts is
currently setting authorization to an empty string when token is falsy; change
the headers construction so the authorization header is only added when token is
present (use a conditional spread or conditional property addition around the
authorization value based on token) so that when unauthenticated the header is
omitted entirely instead of sending authorization: ''. Target the headers
creation logic and the token variable in builder.ts to implement this
conditional addition.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b17da6b4-1970-4bb1-8890-9a8127e92674
📒 Files selected for processing (10)
cypressFetchMock.cjssrc/app/(main)/claims/[id]/route.tssrc/app/(main)/claims/route.tssrc/app/(main)/layout.tsxsrc/components/Claim/Claim.tsxsrc/data/queries/claimQuery.tssrc/utils/apolloClient/apolloClient.tssrc/utils/apolloClient/builder.tssrc/utils/auth.tssrc/utils/dataciteGraphql.ts
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/components/Claim/Claim.tsx (1)
46-64:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSurface mutation failures in
claimErrorviaonError.
onCreateandonDeleteonly updateclaimErrorinonSuccess. If the mutation request fails, users get no actionable feedback. AddonErrorhandlers and map the thrown error message intosetClaimError.Proposed patch
const onCreate = () => { createClaim( { doi: doi_id, sourceId: 'orcid_search' }, { onSuccess: (result) => { setClaimError(result.claim?.errorMessages?.[0]?.title || null) }, + onError: (error) => { + setClaimError(error instanceof Error ? error.message : 'Failed to create claim') + }, } ) } @@ deleteClaim(claim.id, { onSuccess: (result) => { setClaimError(result.claim?.errorMessages?.[0]?.title || null) }, + onError: (error) => { + setClaimError(error instanceof Error ? error.message : 'Failed to delete claim') + }, }) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Claim/Claim.tsx` around lines 46 - 64, onCreate and onDelete only set claimError on successful mutation; add onError handlers to both createClaim and deleteClaim calls to surface failures into UI by calling setClaimError with the thrown error message. Update the createClaim invocation in onCreate and the deleteClaim invocation in onDelete to include an onError: (error) => setClaimError(error?.message || (error?.response?.data?.message) || String(error)) handler (preserving the existing onSuccess behavior), so both mutation paths update claimError when they fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@src/components/Claim/Claim.tsx`:
- Around line 46-64: onCreate and onDelete only set claimError on successful
mutation; add onError handlers to both createClaim and deleteClaim calls to
surface failures into UI by calling setClaimError with the thrown error message.
Update the createClaim invocation in onCreate and the deleteClaim invocation in
onDelete to include an onError: (error) => setClaimError(error?.message ||
(error?.response?.data?.message) || String(error)) handler (preserving the
existing onSuccess behavior), so both mutation paths update claimError when they
fail.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 5d0e33a0-af28-4960-b8ab-6b5eb614a80d
📒 Files selected for processing (8)
src/app/(main)/claims/[id]/route.tssrc/app/(main)/claims/route.tssrc/app/(main)/layout.tsxsrc/components/Claim/Claim.tsxsrc/data/queries/claimQuery.tssrc/utils/apolloClient/builder.tssrc/utils/auth.tssrc/utils/dataciteGraphql.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/Claim/Claim.tsx (1)
132-134:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winDelete mutation errors are not displayed when already claimed.
When
isClaimedis true and the delete fails,claimErroris populated but this paragraph won't render because!isClaimedis false. The user sees no feedback.Widen the condition so errors surface regardless of claim state:
Proposed fix
- {!isClaimed && ( + {(!isClaimed || errorMessage) && ( <p className='secondary px-4 mt-2'>{getHelpContent(user, errorMessage)}</p> )}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Claim/Claim.tsx` around lines 132 - 134, The current conditional rendering uses !isClaimed so when a delete fails (claimError set) no message is shown; update the condition around the paragraph that calls getHelpContent(user, errorMessage) so it renders if the claim is not active OR there is a delete/claim error (e.g. (!isClaimed || claimError) or similar), ensuring getHelpContent still receives the existing user and errorMessage values; locate the JSX that contains isClaimed, claimError, errorMessage, and getHelpContent in Claim (Claim.tsx) to make this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/components/Claim/Claim.tsx`:
- Around line 132-134: The current conditional rendering uses !isClaimed so when
a delete fails (claimError set) no message is shown; update the condition around
the paragraph that calls getHelpContent(user, errorMessage) so it renders if the
claim is not active OR there is a delete/claim error (e.g. (!isClaimed ||
claimError) or similar), ensuring getHelpContent still receives the existing
user and errorMessage values; locate the JSX that contains isClaimed,
claimError, errorMessage, and getHelpContent in Claim (Claim.tsx) to make this
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b1c7aad3-c3e1-4479-a425-42b9b1fdf713
📒 Files selected for processing (1)
src/components/Claim/Claim.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/Claim/Claim.tsx`:
- Around line 132-133: The help <p> is being rendered whenever (!isClaimed ||
errorMessage) even if getHelpContent(user, errorMessage) returns null; compute
the help content once (call getHelpContent(user, errorMessage) into a local
variable inside the Claim component) and change the render condition to only
render the <p className='secondary px-4 mt-2'> when that variable is
non-null/defined (i.e., render when helpContent exists), referencing
getHelpContent, isClaimed, and errorMessage to locate the code.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: da3d3a24-4a92-40f8-bdea-4952d97c1e22
📒 Files selected for processing (1)
src/components/Claim/Claim.tsx
akita
|
|||||||||||||||||||||||||||||||||||||
| Project |
akita
|
| Branch Review |
master
|
| Run status |
|
| Run duration | 03m 53s |
| Commit |
|
| Committer | Joseph Rhoads |
| View all properties for this run ↗︎ | |
| Test results | |
|---|---|
|
|
2
|
|
|
0
|
|
|
0
|
|
|
0
|
|
|
43
|
| View all changes introduced in this branch ↗︎ | |
Tests for review

cypress/e2e/personContainer.test.ts • 2 failed tests • Tests
| Test | Artifacts | |
|---|---|---|
| PersonContainer > facetlist-group |
Test Replay
Screenshots
|
|
| PersonContainer > production chart |
Test Replay
Screenshots
|
|
Purpose
This PR moves the GraphQL calls for creating, deleting, and fetching claims from the frontend to the backend. This improves security and centralization of data handling.
Approach
Instead of making direct GraphQL requests from the frontend using Apollo Client, this PR introduces new API routes in Next.js (
/claimsand/claims/[id]) that act as intermediaries. These backend routes now handle the communication with the DataCite GraphQL API.The
cypressFetchMock.cjsfile has been updated to mock these new backend API routes, ensuring that Cypress tests continue to function correctly.The frontend components have been updated to use React Query (
@tanstack/react-query) for managing API calls and caching, replacing the previous Apollo Client implementation for claim-related data.Key Modifications
src/app/(main)/claims/route.ts: Handles GET (fetch claims) and POST (create claim) requests.src/app/(main)/claims/[id]/route.ts: Handles DELETE (delete claim) requests.cypressFetchMock.cjs: Updated to mock the new backend API routes instead of direct GraphQL calls.src/data/queries/claimQuery.ts: Rewritten to use@tanstack/react-queryhooks (useQuery,useMutation) and fetch data from the new backend API routes.src/utils/auth.ts: New utility to abstract the retrieval of the authentication token from cookies.src/utils/apolloClient/apolloClient.ts: Updated to use the newgetAuthTokenutility.src/utils/dataciteGraphql.ts: New utility to handle generic GraphQL requests to the DataCite API.src/app/(main)/layout.tsx: Updated to use the newgetAuthTokenutility.src/components/Claim/Claim.tsx: Updated to use the new React Query hooks and interact with the backend API routes.Important Technical Details
getAuthTokenutility centralizes the logic for retrieving the access token, making it reusable across different parts of the application.Types of changes
Reviewer, please remember our guidelines:
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Tests