feat: implement leaderboard api layer#61
Conversation
📝 WalkthroughWalkthroughAdds leaderboard types, mock data, a client API module, React Query hooks, and three Next.js GET API routes (paginated leaderboard, top contributors, user rank) that simulate latency and return mocked JSON responses. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client (React UI)
participant Hooks as React Query Hooks
participant API as Next.js API Route
participant Mock as Mock Data Module
Client->>Hooks: request leaderboard/top/user
Hooks->>API: HTTP GET /api/leaderboard* or /top or /user/{id}
API->>Mock: call getMockLeaderboard / getMockUserRank
Mock-->>API: return mocked data
API-->>Hooks: JSON response (entries / contributor / top list)
Hooks-->>Client: cached query result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 🧪 Unit Test Generation v2 is now available!We have significantly improved our unit test generation capabilities. To enable: Add this to your reviews:
finishing_touches:
unit_tests:
enabled: trueTry it out by using the Have feedback? Share your thoughts on our Discord thread! Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@hooks/use-leaderboard.ts`:
- Around line 46-55: The prefetch in usePrefetchLeaderboardPage currently calls
queryClient.prefetchQuery but the leaderboard hook uses useInfiniteQuery; change
the call to queryClient.prefetchInfiniteQuery in usePrefetchLeaderboardPage,
update the queryFn to accept ({ pageParam = 1 }) and call
leaderboardApi.fetchLeaderboard(filters, { page: pageParam, limit }), provide
initialPageParam: 1 and a getNextPageParam that returns the next page number
while loaded entries < lastPage.totalCount, and use the pages/pagination options
to prefetch up to the requested page; keep references to
LEADERBOARD_KEYS.list(filters) and leaderboardApi.fetchLeaderboard so the
correct key/function are used.
In `@lib/api/leaderboard.ts`:
- Around line 16-20: Remove the unused timeframe param from the request params
in lib/api/leaderboard.ts: update the params object (currently referencing
pagination.page, pagination.limit, filters.timeframe) to only include page and
limit so it matches what app/api/leaderboard/route.ts expects; locate the params
variable construction and delete the timeframe entry (filters.timeframe) and any
related references so only page and limit are sent.
🧹 Nitpick comments (6)
app/api/leaderboard/route.ts (1)
7-9: Add input validation for query parameters.The
tierparameter is cast without validation. If an invalid tier value is passed (e.g.,?tier=INVALID), it will be forwarded togetMockLeaderboardand silently return no results since no contributors match the invalid tier. Consider validating against theReputationTiertype.🛡️ Proposed validation
+const VALID_TIERS = ['NEWCOMER', 'CONTRIBUTOR', 'ESTABLISHED', 'EXPERT', 'LEGEND'] as const; + export async function GET(request: Request) { const { searchParams } = new URL(request.url); - const page = parseInt(searchParams.get('page') || '1'); - const limit = parseInt(searchParams.get('limit') || '10'); - const tier = searchParams.get('tier') as ReputationTier | null; + const page = parseInt(searchParams.get('page') || '1', 10); + const limit = parseInt(searchParams.get('limit') || '10', 10); + const tierParam = searchParams.get('tier'); + const tier = tierParam && VALID_TIERS.includes(tierParam as ReputationTier) + ? (tierParam as ReputationTier) + : undefined;app/api/leaderboard/user/[userId]/route.ts (1)
21-25: Inconsistent response field name and potential type mismatch.The field
paramsUserIdon line 24 should likely beuserIdfor consistency. Also, the API client inlib/api/leaderboard.tsexpects{ rank, contributor }but this returns an extra field. While this won't break functionality, the naming is unclear.✏️ Suggested fix
return NextResponse.json({ rank: result.rank, contributor: result.contributor, - paramsUserId: userId + userId });app/api/leaderboard/top/route.ts (1)
4-14: Consider adding an upper bound for thecountparameter.There's no validation preventing extremely large
countvalues. While this is mock data, adding a reasonable upper bound would be a good practice for when real data is integrated.🛡️ Proposed validation
export async function GET(request: Request) { const { searchParams } = new URL(request.url); - const count = parseInt(searchParams.get('count') || '5'); + const count = Math.min(parseInt(searchParams.get('count') || '5', 10), 100);lib/mock-leaderboard.ts (2)
11-11: Usesubstringinstead of deprecatedsubstr.
String.prototype.substris deprecated. Usesubstringorsliceinstead.✏️ Proposed fix
- walletAddress: `0x${Math.random().toString(16).substr(2, 40)}`, + walletAddress: `0x${Math.random().toString(16).substring(2, 42)}`,
67-74: Rank calculation may be inconsistent withgetMockLeaderboard.
getMockUserRankreturns rank based on array index position inmockLeaderboardData, whilegetMockLeaderboardsorts bytotalScorebefore pagination. If the mock data array order doesn't match score order, or if tier filtering is applied, the ranks could be inconsistent between endpoints.For the current mock data this works because contributors are roughly ordered by score, but consider sorting before index lookup for consistency:
✏️ Proposed fix for consistency
export const getMockUserRank = (userId: string) => { - const index = mockLeaderboardData.findIndex(u => u.userId === userId); + const sorted = [...mockLeaderboardData].sort((a, b) => b.totalScore - a.totalScore); + const index = sorted.findIndex(u => u.userId === userId); if (index === -1) return null; return { rank: index + 1, - contributor: mockLeaderboardData[index] + contributor: sorted[index] }; };types/leaderboard.ts (1)
1-11: Consider adding runtime enums (const object + union) to match spec and enable validation.These are type-only, so there’s no runtime value for validation/serialization or to align with the issue’s “enum” wording. A const-object + union keeps string literal compatibility while providing runtime values.
💡 Suggested pattern
-export type ReputationTier = - | 'NEWCOMER' - | 'CONTRIBUTOR' - | 'ESTABLISHED' - | 'EXPERT' - | 'LEGEND'; +export const ReputationTier = { + NEWCOMER: 'NEWCOMER', + CONTRIBUTOR: 'CONTRIBUTOR', + ESTABLISHED: 'ESTABLISHED', + EXPERT: 'EXPERT', + LEGEND: 'LEGEND', +} as const; +export type ReputationTier = typeof ReputationTier[keyof typeof ReputationTier]; -export type LeaderboardTimeframe = - | 'ALL_TIME' - | 'THIS_MONTH' - | 'THIS_WEEK'; +export const LeaderboardTimeframe = { + ALL_TIME: 'ALL_TIME', + THIS_MONTH: 'THIS_MONTH', + THIS_WEEK: 'THIS_WEEK', +} as const; +export type LeaderboardTimeframe = + typeof LeaderboardTimeframe[keyof typeof LeaderboardTimeframe];
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@lib/api/leaderboard.ts`:
- Around line 11-24: The fetchLeaderboard implementation in leaderboardApi is
omitting filters.timeframe from the request params so different timeframes
produce identical results; update the params object in
leaderboardApi.fetchLeaderboard to include filters.timeframe when present (e.g.,
add a conditional that sets params.timeframe = filters.timeframe) so the GET to
LEADERBOARD_ENDPOINT sends the timeframe filter along with page/limit/tier/tags.
This pull request introduces a complete mock leaderboard feature, including backend API endpoints, frontend hooks, and supporting types and utilities. The changes enable fetching, filtering, and paginating leaderboard data, retrieving user ranks, and obtaining top contributors, all using mock data for development and testing purposes.
Backend API Endpoints:
/api/leaderboardendpoint to provide paginated and filterable leaderboard data, simulating network latency and returning mock data in a structured response./api/leaderboard/topendpoint to fetch the top N contributors, supporting a configurable count parameter and mock delay./api/leaderboard/user/[userId]endpoint to fetch a specific user's leaderboard rank and contributor data, with error handling for missing users. (app/api/leaderboard/user/[userId]/route.tsR1-R26)Mock Data and Utilities:
lib/mock-leaderboard.tsto generate and serve mock leaderboard data, including contributor details, stats, and utility functions for filtering, pagination, and user rank lookup.Frontend Hooks and API Client:
hooks/use-leaderboard.tswith React Query hooks for fetching leaderboard lists, user rank, and top contributors, including prefetching support and consistent query keys.lib/api/leaderboard.tsAPI client to interface with the new endpoints, supporting filters, pagination, and type-safe responses.Type Definitions:
types/leaderboard.ts, covering contributors, entries, filters, pagination, and reputation tiers.Other Minor Change:
milestonesproperty intypes/bounty.tsfromany[]tounknown[]for improved type safety.closes #57
Summary by CodeRabbit
New Features
Improvements
Style
✏️ Tip: You can customize this high-level summary in your review settings.