Skip to content

Optimize playlist memberships fetching with batched GraphQL query#819

Open
marcodejongh wants to merge 1 commit intomainfrom
claude/fix-n-plus-one-query-RDOUJ
Open

Optimize playlist memberships fetching with batched GraphQL query#819
marcodejongh wants to merge 1 commit intomainfrom
claude/fix-n-plus-one-query-RDOUJ

Conversation

@marcodejongh
Copy link
Owner

Summary

Refactored playlist data fetching to use React Query for state management and introduced a new batched GraphQL query to fetch playlist memberships for multiple climbs in a single request, replacing N separate queries with one optimized call.

Key Changes

Frontend (packages/web/app/hooks/use-climb-actions-data.tsx):

  • Removed local useState hooks for playlists, playlistMemberships, and playlistsLoading
  • Migrated to useQuery for both user playlists and playlist memberships, providing automatic caching and consistency
  • Replaced individual playlistsForClimb queries with a single batched GET_PLAYLIST_MEMBERSHIPS_FOR_CLIMBS query
  • Updated optimistic updates to use queryClient.setQueryData() instead of state setters
  • Improved mutation callbacks to properly handle cache updates with immutable Set operations
  • Simplified refreshPlaylists() to use queryClient.invalidateQueries()

Backend (packages/backend/src/graphql/resolvers/playlists/queries.ts):

  • Added new playlistMembershipsForClimbs resolver that accepts multiple climb UUIDs
  • Executes a single database query to fetch all playlist memberships for all requested climbs
  • Returns results grouped by climb UUID with empty arrays for climbs with no memberships

GraphQL Schema (packages/shared-schema/src/schema.ts):

  • Added GetPlaylistMembershipsForClimbsInput input type for batch requests
  • Added PlaylistMembershipEntry type to represent climb-to-playlists mapping
  • Added playlistMembershipsForClimbs query field to the Query type

GraphQL Operations (packages/web/app/lib/graphql/operations/playlists.ts):

  • Added GET_PLAYLIST_MEMBERSHIPS_FOR_CLIMBS query definition
  • Added TypeScript interfaces for the new query and its response types

Validation (packages/backend/src/validation/schemas.ts):

  • Added GetPlaylistMembershipsForClimbsInputSchema with validation for board type, layout ID, and climb UUIDs (max 200)

Notable Implementation Details

  • Query keys are memoized to prevent unnecessary re-renders and cache invalidations
  • Stale time set to 5 minutes for both playlists and memberships queries
  • Window focus refetching disabled to avoid unnecessary network requests
  • Optimistic updates create new Set instances to avoid stale closure mutations
  • Batch query supports up to 200 climb UUIDs per request
  • Gracefully handles empty climb lists and missing memberships

https://claude.ai/code/session_013FeKjWGrgDj3N2oq9kvU6R

Replace N separate GET_PLAYLISTS_FOR_CLIMB GraphQL requests with a single
batched playlistMembershipsForClimbs query. This adds:

- New GraphQL query, input type, and return type in shared schema
- Backend resolver using a single SQL query with inArray for batch lookup
- Validation schema for the batch input (max 200 UUIDs)
- Client-side operation and TypeScript types

Also fixes stale closure risk in addToPlaylist/removeFromPlaylist callbacks
by creating new Sets instead of mutating existing ones in-place, and
refactors the playlists fetch from useEffect+useState to useQuery for
consistency with the rest of the hook and automatic caching/deduplication.

https://claude.ai/code/session_013FeKjWGrgDj3N2oq9kvU6R
@vercel
Copy link

vercel bot commented Feb 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
boardsesh Building Building Feb 13, 2026 2:24pm

Request Review

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@claude
Copy link

claude bot commented Feb 13, 2026

Claude Review

Ready to merge - Minor issues noted below, but nothing blocking.

Issues

  1. Validation mismatch - packages/backend/src/validation/schemas.ts:444: Schema requires .min(1) but the resolver at packages/backend/src/graphql/resolvers/playlists/queries.ts:327-329 has a redundant early return for empty arrays that will never execute since validation rejects empty arrays first. This dead code is harmless but indicates a minor inconsistency.

  2. Missing token in queryFn - packages/web/app/hooks/use-climb-actions-data.tsx:176: The queryFn uses token from outer closure but token isn't in the query key. If the token changes while the query key remains the same, stale data could be returned from cache. Consider adding token-dependent logic to the enabled condition (already partially handled since playlistsEnabled checks isAuthenticated).

  3. No tests added - This PR introduces a new GraphQL query with database access and frontend integration but includes no tests. The batch query resolver and cache update logic are good candidates for unit tests.

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.

2 participants