Skip to content

feat: implement leaderboard api layer#61

Merged
0xdevcollins merged 4 commits intoboundlessfi:mainfrom
Dprof-in-tech:feat-implement-leaderboard-api-layer
Jan 30, 2026
Merged

feat: implement leaderboard api layer#61
0xdevcollins merged 4 commits intoboundlessfi:mainfrom
Dprof-in-tech:feat-implement-leaderboard-api-layer

Conversation

@Dprof-in-tech
Copy link
Contributor

@Dprof-in-tech Dprof-in-tech commented Jan 29, 2026

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:

  • Added /api/leaderboard endpoint to provide paginated and filterable leaderboard data, simulating network latency and returning mock data in a structured response.
  • Added /api/leaderboard/top endpoint to fetch the top N contributors, supporting a configurable count parameter and mock delay.
  • Added /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:

  • Implemented lib/mock-leaderboard.ts to 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:

  • Added hooks/use-leaderboard.ts with React Query hooks for fetching leaderboard lists, user rank, and top contributors, including prefetching support and consistent query keys.
  • Implemented lib/api/leaderboard.ts API client to interface with the new endpoints, supporting filters, pagination, and type-safe responses.

Type Definitions:

  • Introduced comprehensive leaderboard-related types in types/leaderboard.ts, covering contributors, entries, filters, pagination, and reputation tiers.

Other Minor Change:

  • Updated the milestones property in types/bounty.ts from any[] to unknown[] for improved type safety.

closes #57

Summary by CodeRabbit

  • New Features

    • Mock leaderboard API: paginated listings with tier/timeframe filtering and tag support.
    • Top contributors list and per-user rank lookup endpoints.
    • Client hooks and helpers for fetching, infinite-scrolling, and prefetching leaderboard pages, top contributors, and individual ranks.
  • Improvements

    • New leaderboard types and response shapes for consistent data.
    • Narrowed bounty milestone type for stronger type safety.
  • Style

    • Minor internal variable declaration refinement.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Backend API Endpoints
app/api/leaderboard/route.ts, app/api/leaderboard/top/route.ts, app/api/leaderboard/user/[userId]/route.ts
New GET route handlers: paginated/filtered leaderboard (/api/leaderboard), top N contributors (/api/leaderboard/top), and user rank lookup (/api/leaderboard/user/[userId]); each parses query/params, simulates latency, and returns mocked JSON payloads.
API Service Layer
lib/api/leaderboard.ts
New leaderboardApi exporting fetchLeaderboard, fetchUserRank, and fetchTopContributors, which serialize query params and call the new API endpoints, returning typed responses.
React Query Hooks
hooks/use-leaderboard.ts
New hooks and key constants: useLeaderboard (infinite query with paging), useUserRank (conditional), useTopContributors, and usePrefetchLeaderboardPage (prefetch helper); configures stale times and pagination behavior.
Mock Data Generator
lib/mock-leaderboard.ts
New mock dataset and helpers: mockLeaderboardData, getMockLeaderboard(page, limit, tier), and getMockUserRank(userId) to support API responses and pagination.
Type Definitions
types/leaderboard.ts
New TypeScript types/interfaces: ReputationTier, LeaderboardTimeframe, ContributorStats, LeaderboardContributor, LeaderboardEntry, LeaderboardResponse, LeaderboardFilters, LeaderboardPagination.
Minor Type Update
types/bounty.ts
Narrowed Bounty.milestones from any[] to unknown[].
Small Declaration Change
app/api/bounties/[id]/milestones/advance/route.ts
Changed variable declaration from let to const for updates while retaining the same initial value and mutable usage.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through code with eager delight,
Mocked ranks in rows, each score shining bright,
Pages and top lists, a user's place found,
Hooks cache and prefetch — I bounded around,
Carrots of bytes, leaderboard dreams abound.

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The change to types/bounty.ts (milestones: any[] → unknown[]) is a minor type improvement unrelated to the leaderboard feature scope and issue #57 objectives. Remove the types/bounty.ts change or create a separate PR for unrelated type improvements to maintain clear scope alignment with issue #57.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: implement leaderboard api layer' clearly and specifically describes the main change of adding leaderboard API infrastructure.
Linked Issues check ✅ Passed All coding requirements from issue #57 are met: TypeScript types defined, API service layer implemented with three methods, React Query hooks created, and all backend endpoints added with mock data.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 tier parameter is cast without validation. If an invalid tier value is passed (e.g., ?tier=INVALID), it will be forwarded to getMockLeaderboard and silently return no results since no contributors match the invalid tier. Consider validating against the ReputationTier type.

🛡️ 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 paramsUserId on line 24 should likely be userId for consistency. Also, the API client in lib/api/leaderboard.ts expects { 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 the count parameter.

There's no validation preventing extremely large count values. 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: Use substring instead of deprecated substr.

String.prototype.substr is deprecated. Use substring or slice instead.

✏️ 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 with getMockLeaderboard.

getMockUserRank returns rank based on array index position in mockLeaderboardData, while getMockLeaderboard sorts by totalScore before 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];

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@0xdevcollins 0xdevcollins merged commit b91a642 into boundlessfi:main Jan 30, 2026
2 checks passed
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.

Implement Leaderboard API Layer and Core Data Types with React Query

2 participants