Skip to content

fix(api): clamp activity and review pagination#339

Merged
ralyodio merged 1 commit into
profullstack:masterfrom
Jorel97:codex/fix-activity-review-pagination-338
May 29, 2026
Merged

fix(api): clamp activity and review pagination#339
ralyodio merged 1 commit into
profullstack:masterfrom
Jorel97:codex/fix-activity-review-pagination-338

Conversation

@Jorel97
Copy link
Copy Markdown
Contributor

@Jorel97 Jorel97 commented May 29, 2026

Summary

  • add a small shared API pagination parser that defaults malformed values and clamps bounds
  • use it on activity and review feed endpoints before building Supabase ranges
  • cover missing, non-numeric, negative, high, and fractional pagination inputs

Fixes #338.

Verification

  • Not run locally: this Codex workspace has Node available but no npm/package runner installed, so I could not execute the repo test suite here.

@ralyodio ralyodio merged commit fcb2784 into profullstack:master May 29, 2026
4 checks passed
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR introduces a shared parsePaginationParam helper that correctly handles malformed, negative, fractional, and out-of-range pagination inputs, and replaces ad-hoc inline parsing across four API routes. It also removes two per-file duplicate helpers from the [username] routes.

  • New helper (src/lib/api-pagination.ts): uses Number() + Math.trunc + clamp, with tests covering null, non-numeric, negative, high, and fractional inputs.
  • Four route files updated: activity and reviews endpoints for both the authenticated-user and per-username paths all switch to the shared helper — no other behavior change in three of the four files.
  • [username]/reviews regression: the separate Supabase query that fetched all ratings for computing average_rating was removed; the average is now derived only from the current page's slice, producing an incorrect value whenever total_reviews > limit.

Confidence Score: 3/5

Safe to merge for the pagination fix itself, but the reviews endpoint now returns a wrong average rating on any paginated request where there are more reviews than one page.

The [username]/reviews handler previously made a dedicated query to fetch every rating before computing the mean. That query was dropped here, leaving average_rating calculated from the current page's slice instead of all reviews. A user with 50 reviews browsing page 2 will see a different — and incorrect — average than a user on page 1, and neither value matches the true global mean. The rest of the changes (the shared helper and its tests, the other three route updates) are straightforward and correct.

src/app/api/users/[username]/reviews/route.ts needs the average-rating calculation fixed before merging.

Important Files Changed

Filename Overview
src/lib/api-pagination.ts New shared pagination helper — correctly handles null/empty/non-numeric/fractional/out-of-range inputs and clamps to [min, max].
src/lib/api-pagination.test.ts Unit tests for the new helper covering null, non-numeric, out-of-range, and fractional values — good coverage for the pagination utility itself.
src/app/api/users/[username]/reviews/route.ts Pagination parsing migrated correctly, but the removal of the all-ratings query means average_rating is now computed from the current page only instead of all reviews.
src/app/api/activity/route.ts Pagination parsing migrated to shared helper with proper bounds; no other functional changes.
src/app/api/reviews/route.ts Pagination parsing migrated to shared helper with proper bounds; no other functional changes.
src/app/api/users/[username]/activity/route.ts Pagination parsing migrated to shared helper; whitespace cleanup only beyond that change.

Sequence Diagram

sequenceDiagram
    participant C as Client
    participant R as /api/users/[username]/reviews
    participant S as Supabase

    C->>R: "GET ?limit=10&offset=0"
    R->>R: parsePaginationParam(limit, 10, 1, 50)
    R->>R: parsePaginationParam(offset, 0, 0, 100_000)
    R->>S: profiles.select("id").eq("username", ...)
    S-->>R: profile
    R->>S: "reviews.select(*).range(offset, offset+limit-1) [count:exact]"
    S-->>R: reviews (page slice) + count (total)
    Note over R: ⚠️ average_rating computed from page slice only, not all reviews
    R-->>C: "{ data, summary: { average_rating, total_reviews }, pagination }"
Loading

Reviews (1): Last reviewed commit: "fix(api): clamp activity and review pagi..." | Re-trigger Greptile

Comment on lines 55 to 61
// Calculate average rating from all reviews
const totalReviews = count || 0;
let averageRating = 0;
if (allRatings && allRatings.length > 0) {
const sumRatings = allRatings.reduce((sum, r) => sum + r.rating, 0);
averageRating = sumRatings / allRatings.length;
if (reviews && reviews.length > 0) {
const sumRatings = reviews.reduce((sum, r) => sum + r.rating, 0);
averageRating = totalReviews > 0 ? sumRatings / reviews.length : 0;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 average_rating computed from page slice, not all reviews

The old code issued a second Supabase query (allRatings) that fetched every rating row for the reviewee before computing the mean. That query was removed here; reviews is now the page-sized subset returned by .range(offset, offset + limit - 1). For a user with 50 total reviews and the default limit=10, averageRating will reflect only the 10 reviews on the current page, returning a different (and wrong) value on every page. The comment on line 55 still says "from all reviews" but that is no longer true.

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.

Activity and review feeds accept invalid pagination values

2 participants