Welcome back! So far, you’ve learned how to build API endpoints that let users retrieve recipes, search by ingredients, and view recipe steps. These features are essential for any recipe app, but sometimes users want a little more excitement or guidance. For example, what if someone can’t decide what to cook and wants a random suggestion? Or maybe they want to see the most popular recipes based on ratings from other users.
In this lesson, you’ll learn how to add two new features to your cooking helper API:
- An endpoint that returns a random recipe
- An endpoint that returns the most popular recipes based on user ratings
These features make your app more interactive and helpful, giving users new ways to discover recipes.
Before we dive in, let’s quickly remind ourselves how we’ve been working with recipes so far.
Previously, you built endpoints to:
- List all recipes with pagination (so users don’t get overwhelmed by too many results at once)
- Retrieve a single recipe by its ID
- Search for recipes by ingredients
All of these endpoints use Express for routing and Prisma to interact with the database. You’ve also learned how to format JSON responses so that the data is easy to use in any app.
Now, let’s build on these skills to add dynamic selection features.
Let’s start by creating an endpoint that returns a random recipe. This is useful for users who want to try something new or can’t decide what to cook.
First, we need to define a new route in our Express app. We’ll use the Express router to create a GET endpoint at /api/recipes/random.
This sets up the endpoint, but it doesn’t do anything yet.
To select a random recipe from the database, we use Prisma’s count method to get the total number of recipes, then generate a random number to skip that many records, and finally use findFirst to retrieve the recipe.
prisma.recipe.count()gets the total number of recipes.Math.floor(Math.random() * total)picks a random index to skip.prisma.recipe.findFirst({ skip, ... })retrieves the recipe at that random position, including its ingredients.
If there are no recipes in the database, recipe will be null.
Now, let’s check if we found a recipe. If not, we return an error message. If we did, we return the recipe details as JSON using Express’s res.status().json().
- If no recipe is found, we return a 404 error.
- Otherwise, we build a JSON object with the recipe’s ID, name, list of ingredient names, and steps.
Here’s the complete function using Express and Prisma:
Example Output:
This endpoint gives users a fun way to discover new recipes with a single click.
Grouping is an important concept when working with data in Prisma. Let’s look at what it means and how it can help us build new features for our API.
To find the most popular recipes, we need to look at all the reviews and calculate the average rating for each recipe. This is where grouping comes in. Grouping is a concept from SQL (the GROUP BY clause) where you organize rows that have the same value in a specific column into groups. For example, if you group reviews by recipeId, you get all reviews for each recipe grouped together.
In Unit 1, you used aggregate functions like count, avg, or sum to get a single value for a whole table or a filtered set (e.g., the average rating for all reviews). Grouping lets you calculate aggregates for each group separately. For example, you can get the average rating for each recipe, not just the overall average.
- Aggregate: "What is the average rating of all reviews?"
- Group By: "What is the average rating for each recipe?"
Prisma’s groupBy lets you use built-in aggregate functions like _avg to automatically calculate averages for each group. This is much more efficient than fetching all reviews and calculating averages in your application code. Prisma and the database handle the heavy lifting for you.
by: Specifies the field(s) to group by (e.g.,recipeId)._avg: Tells Prisma to calculate the average of a field (e.g.,rating) for each group.orderBy: Sorts the groups by the calculated aggregate (e.g., highest average rating first).
Next, let’s create an endpoint that returns the most popular recipes. We’ll define “popular” as recipes with the highest average user ratings.
We’ll create a GET endpoint at /api/recipes/popular using the Express router.
To find popular recipes, we need to:
- Group reviews by
recipeId - Calculate the average rating for each group using
_avg - Sort the groups by average rating in descending order
- Limit the results to the top 10 recipes
Here’s how you do this with Prisma’s groupBy:
by: ["recipeId"]means we want to group all reviews by which recipe they belong to._avg: { rating: true }tells Prisma to calculate the average rating for each group.orderBy: { _avg: { rating: "desc" } }sorts the groups so the recipes with the highest average ratings come first.take: 10limits the results to the top 10 recipes.
Now, we need to fetch the recipe details for these top-rated recipes and combine them with their average ratings.
- We fetch all recipes whose IDs are in the top-rated list.
- We map each group to its recipe and format the output, including the average rating rounded to two decimal places.
Here’s the complete function using Express and Prisma:
Example Output:
This endpoint helps users quickly find the best-rated recipes, making it easier to choose something delicious.
In this lesson, you learned how to add two new endpoints to your recipe API:
- One for serving a random recipe to help users discover new dishes
- One for listing the most popular recipes based on user ratings
You also learned how grouping works in Prisma, how it differs from aggregate, and how to use _avg, by, and orderBy together to efficiently calculate and sort average ratings. Now, you’re ready to practice building and using these endpoints in the exercises that follow. This will help you reinforce what you’ve learned and see how these features work in real scenarios.
Keep up the great work — your cooking helper app is becoming more powerful with each lesson!
