Build the Summary Endpoint
Raw data is great when you need specifics. But sometimes you want the big picture. How many people left feedback? What's the average rating? Which course is struggling?
Nobody wants to curl 200 entries and do math in their head. Let's build an endpoint that does the math for us.
Outcome
Create a GET /api/feedback/summary route that returns aggregate statistics.
Fast Track
- Open
app/api/feedback/summary/route.ts(stub provided in starter) - Calculate totals, averages, and rating distribution
- Group stats by course
Hands-on exercise
Open app/api/feedback/summary/route.ts. The starter has this file with a stub handler. This endpoint supports one optional query parameter: courseSlug, which filters the data before aggregating.
The response shape should look like this:
{
"totalEntries": 10,
"averageRating": 4.2,
"ratingDistribution": {
"1": 0,
"2": 1,
"3": 1,
"4": 3,
"5": 5
},
"courses": [
{
"courseSlug": "knife-skills",
"totalEntries": 5,
"averageRating": 4.4
}
]
}A few implementation notes:
Rating distribution is an object where keys are ratings 1 through 5 and values are counts. Initialize all five keys to zero before counting, so the response always includes every rating level, even if nobody gave a 1.
Average rating should be rounded to one decimal place. Math.round(value * 10) / 10 handles that cleanly.
Per-course breakdown groups entries by courseSlug and computes totalEntries and averageRating for each. A Map works well here since you're building up state while iterating.
Empty state: If there's no feedback (or the courseSlug filter matches nothing), return zeros across the board with an empty courses array. Don't return a 404. An empty summary is a valid summary.
Try It
Full summary:
curl http://localhost:3000/api/feedback/summary{
"totalEntries": 10,
"averageRating": 4.2,
"ratingDistribution": {
"1": 0,
"2": 1,
"3": 1,
"4": 3,
"5": 5
},
"courses": [
{
"courseSlug": "knife-skills",
"totalEntries": 5,
"averageRating": 4.4
},
{
"courseSlug": "bread-baking",
"totalEntries": 4,
"averageRating": 4.0
},
{
"courseSlug": "pasta-from-scratch",
"totalEntries": 1,
"averageRating": 4.0
}
]
}Summary for one course:
curl "http://localhost:3000/api/feedback/summary?courseSlug=knife-skills"Should return stats for just the knife-skills entries.
Summary for a nonexistent course:
curl "http://localhost:3000/api/feedback/summary?courseSlug=underwater-basket-weaving"{
"totalEntries": 0,
"averageRating": 0,
"ratingDistribution": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0 },
"courses": []
}No 404. Zeros are informative.
With this endpoint, nobody has to eyeball 200 entries to figure out which course needs attention. One request, one response, all the numbers that matter.
Commit
git add -A && git commit -m "feat(api): add summary endpoint with aggregate stats"Done-When
GET /api/feedback/summaryreturnstotalEntries,averageRating,ratingDistribution, andcoursesaverageRatingis rounded to one decimal placeratingDistributionalways includes keys 1 through 5courseSlugquery param filters before aggregating- Empty results return zeros, not a 404
Solution
import { NextRequest, NextResponse } from "next/server";
import { getAllFeedback } from "@/lib/data";
export async function GET(request: NextRequest) {
const { searchParams } = request.nextUrl;
const courseSlug = searchParams.get("courseSlug");
let feedback = await getAllFeedback();
if (courseSlug) {
feedback = feedback.filter((fb) => fb.courseSlug === courseSlug);
}
if (feedback.length === 0) {
return NextResponse.json({
totalEntries: 0,
averageRating: 0,
ratingDistribution: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 },
courses: [],
});
}
const avgRating =
feedback.reduce((sum, fb) => sum + fb.rating, 0) / feedback.length;
const distribution = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 } as Record<number, number>;
for (const fb of feedback) {
distribution[fb.rating]++;
}
const courseMap = new Map<string, { count: number; sum: number }>();
for (const fb of feedback) {
const existing = courseMap.get(fb.courseSlug) ?? { count: 0, sum: 0 };
existing.count++;
existing.sum += fb.rating;
courseMap.set(fb.courseSlug, existing);
}
const courses = [...courseMap.entries()].map(([slug, data]) => ({
courseSlug: slug,
totalEntries: data.count,
averageRating: Math.round((data.sum / data.count) * 10) / 10,
}));
return NextResponse.json({
totalEntries: feedback.length,
averageRating: Math.round(avgRating * 10) / 10,
ratingDistribution: distribution,
courses,
});
}Was this helpful?