Welcome to the first lesson of the Building the Recipe API course! In this lesson, you will learn how to build the core endpoints that allow your app to fetch recipes and their details from a database.
Recipe retrieval is a fundamental feature of any cooking helper app. Users need to be able to look up recipes, see what ingredients are required, and follow the steps to cook a dish. By the end of this lesson, you will know how to create endpoints that return a single recipe by its ID and a list of recipes with pagination. These skills are the foundation for more advanced features you’ll add later, such as searching, filtering, and user reviews.
Before we dive in, let’s briefly recall two key concepts you’ll use in this lesson: API routes and database models.
- API routes define the URLs of your web application and map them to specific logic. In this framework, you define the URL patterns in a
urls.pyfile and the logic inside functions calledviewsin aviews.pyfile. For example, when a user visits/api/recipes/1, a view function will handle that request and return the recipe withID1. - Database models are classes that represent tables in your database. For example, you might have a
Recipemodel for recipes and aReviewmodel for user reviews. These models allow you to interact with your data using objects and methods instead of writing raw database queries.
You’ll see both of these concepts in action as we build the recipe retrieval endpoints.
Let’s start by building an endpoint that returns a single recipe by its ID. This is useful when a user wants to view the details of a specific recipe.
You define a route by adding a path to the urlpatterns list in your urls.py file. Here’s how you set up a route to handle requests like /api/recipes/1:
- The
<int:recipe_id>part is a path converter. It matches an integer value in the URL and passes it to theget_recipeview function as an argument namedrecipe_id.
Inside the view function in views.py, you need to fetch the recipe from the database. You can use the model's manager to filter for the specific ID:
Recipe.objects.filter(pk=recipe_id)searches for the recipe.- The
pkstands for primary key, which is often theID. .prefetch_related("ingredients")is used to efficiently load the related ingredients in a single step..first()returns the object if found, orNoneif it doesn't exist. If it is missing, we return aJsonResponsewith a404status.
Suppose your app allows users to rate recipes. You can calculate the average rating by using aggregation:
Review.objects.filter(...)gets all reviews for this specific recipe..aggregate(avg=Avg("rating"))calculates the average of theratingcolumn.- If there are no ratings, the result is
None; otherwise, we round it to two decimal places.
To send data back to the user, you must convert the database object into a format that can be turned into JSON (usually a dictionary). A utility function is a great way to handle this serialization:
- The
serialize_recipefunction extracts the necessary fields into a dictionary. JsonResponsethen takes this dictionary and sends it to the user as a JSON object.
Example Output:
Next, let’s build an endpoint that returns a list of recipes, but only a few at a time. This is called pagination, and it helps your app handle large numbers of recipes efficiently.
First, add the route to urls.py:
In the view function, you can extract pagination parameters from the URL's query string (e.g., /api/recipes?page=2&per_page=5) using request.GET:
request.GET.get("page", 1)attempts to get thepageparameter, defaulting to1if it is missing.
To get the correct slice of data, you calculate an offset and use slicing on the database QuerySet:
totalis the total number of recipes in the database.[offset : offset + per_page]tells the database to skip the recipes from previous pages and only return the number of items specified byper_page.
Finally, construct a dictionary containing the list of recipes and metadata about the pagination:
- We use a list comprehension to call
serialize_recipeon every item in the current page. - The response includes helper data like
has_nextandhas_prevso the frontend knows if it should display "Next" or "Previous" buttons.
Example Output:
In this lesson, you learned how to build two essential endpoints for your cooking helper app:
- One to fetch a single recipe by its
ID, including its ingredients and average rating, by mapping a URL path to aviewfunction. - Another to list all recipes with manual pagination using
QuerySetslicing and query parameters.
These endpoints are the backbone of your app’s recipe retrieval features. In the practice exercises that follow, you’ll get hands-on experience implementing these endpoints, defining routes in urls.py, and returning data using JsonResponse. Good luck, and have fun building!
