Nutrition is an essential aspect of human life but many people have problems with it.
Some don’t have enough time and passion for eating healthy food, while others like to cook the same old dishes.
Today we will solve this issue, improve our diet, and learn how to create a beautiful web application for recipe search using APIs.
How to Create a Food Website with an API
- Choose the right Recipe API
- Add a Search Functionality with the Recipe API
- Create a Search Results Page
- Create a Detailed Recipe Page
If you’re looking to start a food blog, this link may be helpful. (Don’t forget to SEO your food blog, like building backlinks and promotion.)
Spoonacular’s Recipe, Food, & Nutrition API
For our purposes, we’ll be using the Spoonacular API as the targeted data source.
It is a comprehensive database for food information and data.
You can find and share enormous amounts of recipes in real-life.
This API includes over 365,000 recipes, which includes over 80,000 ingredients, making it one of the best recipe APIs that includes full access to recipes and cooking instructions.
Also, the API allows for searching the recipes and cooking procedures via natural language. For example, you can search for something like “vegan desserts”, and you should receive some comprehensive search results.
Spoonacular has powerful calculation features for calories, pricing, and many other aspects. A potential user can even see a visualization of different meals of different cuisines.
Finally, there is a space for custom diets.
You can submit your particular diet, or follow the rules of some specific diet template.
This is only the tip of the iceberg – there are even associated chatbots and an ingredients autocompleting system!
How to use and get an API key to the Spoonacular API
In order to use the Spoonacular API, we will be using the RapidAPI platform.
Simply, sign up for a free RapidAPI user account and subscribe to the API. (hint: there’s a Basic Plan that allows you to do 500 free requests/day).
We’ll be coding in Python for this project, and we will create a Flask application with a bright demonstration of the nutrition API power. So be sure to have Python 3.7 on your machine.
Recipe API Overview
Here are some of the top API endpoints:
- compute requests – calculations. These endpoints can visualize equipment widgets and count all the product nutrition. Powerful instruments for advanced recipe usage. Examples: classify a grocery product, guess nutrition by dish name, recipe summarizing;
- extract requests – parsing and analyzing. Here a user can try to process raw data and get heating elements. Examples: ingredients parsing, detect food in text, analyze recipe from website;
- search requests – Supports many different ways of recipe searching. Examples: get wine pairing, autocomplete product search, get similar recipes;
- data requests – Examples: get recipe information bulk, get product information, get recipe nutrition;
- chat requests – interaction. It is crucial when creating bots. Here, you can test the powers of AI and conversation suggestions. Examples: talk to a chatbot, get conversation suggestions.
The medial side responds to the headers and parameters within the chosen endpoint.
Here, you can check your unique RapidAPI id and host of the tested product.
It is a very informative way to meet a user with a request functionality.
Each input field has a description; you can change and test different combinations of them.
If you would like to see the results of the previously mentioned tests, look to the right side, you can find code snippets for all supported programming languages.
Now let’s review a practical example.
How to Build a Food App or Website using the Spoonacular API
“What’s in your fridge” website
Once in awhile, we’ll peek into our fridge and make some strange food combinations.
Let’s create an app that helps us find recipes given the ingredients we have available.
1. Create a Search Page
First, let’s create a starting point for our users with a search page. If the user chooses not to type anything, the system still will back a few random propositions.
After that, the user can choose and open a detailed view of a single recipe. Each page should include dynamic information and use multiple API endpoints. The full list of them is as follows:
- GET random joke – just a funny way to say hello;
- GET recipes by ingredients’ enumeration – the basic type of recipe search. Ingredients are typed with comma-separator;
- GET random recipes – in case of user’s interest in something new;
- GET recipe information by ID – detailed text info with a recipe image;
- VISUALIZE recipe ingredients by ID – default HTML-widget from Spoonacular for the ingredients demonstration;
- VISUALIZE recipe equipment by ID – one more widget, but for the equipment.
As mentioned before, we will make a web application using Python, so we will need a framework. Luckily, Python has a swift and very lightweight solution called Flask – a microframework, which is also quite an active programming tool. Don’t forget to install it into your Python environment.
pip install flask
The next required library – requests. It is one of the possible solutions for HTTP requests handling.
pip install requests
Now you can create a folder for a web-app and .py file. Let’s use Bootstrap for a prettified HTML layout – an open-sourced library for many CSS templates.
Next, we will create a templates subfolder with all the layout snippets. Each of them will be executed after the backside data processing. You need to have a method-handler per each unique data action. This method should render a template with a dynamic response creating.
Next, let’s use Jinja2 – a method of connecting Python code with HTML templates. It’s a syntax language for Python commands using mark-up. Okay, let’s now code something!
First, let’s create a base.html file that will be a basic template for all of the next steps.
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous"> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script> <title>{% block title %} Base page {% endblock %}</title> </head> <body> {% block body %} Body block {% endblock %} </body> </html>
Here we include all the required style files and create blocks for the dynamic data.
Now we can try to work with the .py file.
Paste the following lines in it:
from flask import Flask, render_template, request import requests app = Flask(__name__) url = "https://spoonacular-recipe-food-nutrition-v1.p.rapidapi.com/" headers = { 'x-rapidapi-host': "spoonacular-recipe-food-nutrition-v1.p.rapidapi.com", 'x-rapidapi-key': "<YOUR_RAPID_API_KEY>", } random_joke = "food/jokes/random" find = "recipes/findByIngredients" randomFind = "recipes/random" @app.route('/') def search_page(): joke_response = str(requests.request("GET", url + random_joke, headers=headers).json()['text']) return render_template('search.html', joke=joke_response) if __name__ == '__main__': app.run()
Now we’ll create some global variables for the endpoints.
Let’s also route the home page with the search.
After receiving the joke, we pass it to the template.
Now create and modify search.html in the following way:
{% extends 'base.html' %} {% block title %} Recipes searcher {% endblock %} {% block body %} <center> <h2 style="padding-top: 3%;"> It's time for cooking! What do you have in the fridge? <br> <small class="text-muted"> Let's warm up with a joke: {{ joke }} </small> </h2> <br><br> <form action="/recipes"> <div class="input-group mb-3" style="max-width: 50%;"> <input type="text" class="form-control" placeholder="Apple, flour, sugar" aria-label="Ingridients" aria-describedby="button-addon2" name="ingridients"> <div class="input-group-append"> <button class="btn btn-outline-secondary" type="submit" id="button-addon2">Search</button> </div> </div> </form> </center> {% endblock %}
Pretty simple layout creation. But let’s try to execute our project by running the .py file.
2. Create a Recipe Search Results Page
Now let’s display the search results.
Let’s display them in a list, so we will need one more method-handler + template.
We can start with the first one.
Paste the following code under the search_page method within the .py file.
@app.route('/recipes') def get_recipes(): if (str(request.args['ingridients']).strip() != ""): # If there is a list of ingridients -> list querystring = {"number":"5","ranking":"1","ignorePantry":"false","ingredients":request.args['ingridients']} response = requests.request("GET", url + find, headers=headers, params=querystring).json() return render_template('recipes.html', recipes=response) else: # Random recipes querystring = {"number":"5"} response = requests.request("GET", url + randomFind, headers=headers, params=querystring).json() print(response) return render_template('recipes.html', recipes=response['recipes'])
Here we will request two different endpoints depending on the search results.
But don’t forget about the templates.
Create a recipes.html file and paste the following lines:
{% extends 'base.html' %} {% block title %} Recipes searcher {% endblock %} {% block body %} <h2 style="text-align: center"> <a href="/" style="text-decoration: none; color:red"> Recipes for you: </a></h2> <div style="margin-left:35%;"> <ul class="list-unstyled"> {% for recipe in recipes %} <li class="media"> <img src="{{recipe['image']}}" class="align-self-center mr-3" alt="..." width="15%" height="15%"> <div class="media-body"> <h5 class="mt-0 mb-1"><a href="/recipe?id={{ recipe['id'] }}">{{ recipe['title'] }}</a></h5> {% if 'likes' not in recipe%} How many minutes for preparation? {{recipe['preparationMinutes']}} <br> How many minutes for cooking? {{recipe['cookingMinutes']}} <br> How many likes has this recipe? {{recipe['aggregateLikes']}} <br> {% else %} How many your ingridients? {{recipe['usedIngredientCount']}} <br> How many missed ingridients? {{recipe['missedIngredientCount']}} <br> How many likes has this recipe? {{recipe['likes']}} <br> {% endif %} </div> </li> <br> {% endfor %} </ul> </div > {% endblock %}
Again, there is a clear separation of two different ways of generating recipes.
Now let’s test it. Let’s try searching for something:
Here are the results for “pork, lemon, honey”.
The user can compare the original recipe and the pointed products.
But how will our app work without any search?
When no search is performed, five random dishes are displayed, including the amount of time required for preparing and cooking.
3. Create detailed Recipe Pages
Now, let’s get a detailed display of the recipes. Let’s start with a usual method in the .py file.
@app.route('/recipe') def get_recipe(): recipe_id = request.args['id'] recipe_info_endpoint = "recipes/{0}/information".format(recipe_id) ingedientsWidget = "recipes/{0}/ingredientWidget".format(recipe_id) equipmentWidget = "recipes/{0}/equipmentWidget".format(recipe_id) recipe_info = requests.request("GET", url + recipe_info_endpoint, headers=headers).json() recipe_headers = { 'x-rapidapi-host': "spoonacular-recipe-food-nutrition-v1.p.rapidapi.com", 'x-rapidapi-key': "<YOUR_RAPID_API_KEY>", 'accept': "text/html" } querystring = {"defaultCss":"true", "showBacklink":"false"} recipe_info['inregdientsWidget'] = requests.request("GET", url + ingedientsWidget, headers=recipe_headers, params=querystring).text recipe_info['equipmentWidget'] = requests.request("GET", url + equipmentWidget, headers=recipe_headers, params=querystring).text return render_template('recipe.html', recipe=recipe_info)
Here you’ll see some interesting new lines of code.
First, let’s execute three requests, instead of one.
Second, we will create sub-headers to satisfy the accept header requirements for the content type.
Finally, we will include a default CSS for the widget integration.
Create a recipe.html template and paste the following snippet:
{% extends 'base.html' %} {% block title %} {{recipe['title']}} {% endblock %} {% block body %} <div style="max-width: 70%; margin: auto; padding-top:3%"> <div class="media"> <img src="{{recipe['image']}}" class="mr-3" alt="..." width="20%" height="20%"> <div class="media-body"> <h5 class="mt-0">{{recipe['title']}}</h5> {{recipe['instructions']}} </div> </div><br> {{recipe['inregdientsWidget'] | safe }}<br> {{recipe['equipmentWidget'] | safe}} </div> {% endblock %}
Voila! The app is completed.
Of course, it isn’t ideal yet, but even now it has many interesting aspects to surf.
The most important thing: “| safe” keyword.
This code indicates HTML markup, so all tags and scripts will be executed. Rerun your project and visit some recipe pages.
Conclusion
Today we have introduced the Spoonacular API.
It is a powerful and multi-functional solution for food nutrition.
We created a visible and robust web application.
The basis of this app was made via RapidAPI endpoints.
Our project can propose fresh recipes depending on the contents of your fridge.
Louis says
Hi,
does it work the same way for Wagtail ? I mean the way to install the API 🙂
Have a good day 🙂
johnvins says
Looks good! Is there a basic folder with all the files to make this usable on a localhost…no way for me to code such a use of an api; plus I can’t afford for someone to write such code.
speed says
This post is really amazing and very informative
your blog inspiration for beginners
Thank for sharing your knowledge