Demo Application

Learn how to build a fully functional React application using the Spoonacular API.

This lesson will walks us through a React application integrated with the Spoonacular API. The following endpoints have been used in the application:

  • search recipes endpoint
  • similar recipes endpoint
  • recipe information endpoint
  • search ingredients endpoint
  • fetch ingredient endpoint
  • ingredient substitute by ID endpoint
  • search products by query string endpoint
  • search products by ingredients endpoint
  • product information endpoint
  • widget endpoints
Press + to interact

Application workflow

This demo application is a food application that provides data on recipes, ingredients, and products as interactive elements. The workflow of the application is as follows:

  1. When we run the application, the landing page is the search page for recipes. There are also similar search pages for ingredients and products. The search results on these pages can be further modified using the provided filters.

  2. The corresponding recipe, ingredient, or product page is opened by clicking any card. Here’s the workflow of each of these pages.

    1. On the specific recipe page, click the “Expand Instructions” button to open the slide set for step-by-step recipe instructions. Use the dropdown menu under the “Recipe Attributes” section to switch between widgets. Finally, we find a list of similar recipes at the very end.

    2. On the specific ingredient page, enter the quantity of the ingredient and click the “Update” button to update the price of the ingredient on the page accordingly. Finally, we find a list of grocery products that contain the specified ingredient.

    3. On the specific product page, there is detailed information about the product. Use the dropdown menu under the “Product Attributes” section to switch between widgets.

Run the application

We can run the demo application by pressing the “Run” button in the widget below. When the React development server starts, click the URL next to “Your app can be found at:” to view the application.

Note: The following widget only shows the files required to explain the Spoonacular API.

import fetch from 'node-fetch';
import express from 'express';
import cors from 'cors';

const app = express();
app.use(cors());

const port = process.env.PORT || 3000;

// Function to make a simple API call
const makeApiCall = async (apiURL, apiOptions) => {
  try {
    // console.log(apiOptions);
    const options = JSON.parse(apiOptions);
    const response = await fetch(apiURL, options);
    console.log(response);
    return response;
  } catch (err) {
    console.error(err);
    return null;
  }
};

app.all('/', async function(req, res, next) {
  try {
    const resObj=await makeApiCall(req.query.apiURL, req.query.apiOptions);
    if (!resObj) {
      res.status(404).send({'message': 'Failed API server no reachable'});
    } else {
      const content = await resObj.text();
      res.status(resObj.status).send(content);
    }
  } catch (err) {
    res.status(404).send({'message': `API server not reachable ${err}`});
  }
  next();
});

app.listen(port);
console.log(`Server started at http://localhost:${port}`);

Code explanation

Let’s dive into the code and see how we’ve integrated different Spoonacular API endpoints into our React application.

To handle CORS for our front-end application, we use a proxy server. We can find the code for it under the backend-server folder. The server takes the endpoint URL and uses fetch options as query parameters from the front-end application. The server also makes the API call to Spoonacular and returns the relevant response.

For our React application, the file index.jsx renders the App component (App.jsx) as the root component. Here’s a brief explanation of the App component:

  • Line 22: We extract the API key stored in the keys.json file and then assign it to the apiKey variable, which is then passed ahead as a prop.

  • Lines 29–36: We set the routes for our application here and pass apiKey as a prop to all relevant pages making an API call.

    • Line 29: We set the home page of our application to the recipes page by redirecting to it when the website path is {EDUCATIVE_APP_URL}/.

Note: We have created and used a custom hook, useFetch, to fetch the data from any API endpoint. To avoid any CORS-related issues, we use a custom proxy server to make API calls.

The general recipes page is rendered by the Recipes component (Recipes.jsx), which displays a search bar and a list of recipes. Here’s a brief explanation of the Recipes component:

  • Lines 48–56: We define the header parameters and options object to make GET requests with a JSON response.

  • Lines 59–78: We declare the query parameters and URL for the search recipes endpoint, which we call using our custom useFetch hook.

The general ingredients page is rendered by the Ingredients component (Ingredients.jsx), which displays a search bar and a list of ingredients. Here's a brief explanation of the Ingredients component:

  • Lines 42–50: We define the header parameters and options object to make GET requests with a JSON response.

  • Lines 53–69: We declare the query parameters and URL for the search ingredients endpoint, which we call using our custom useFetch hook.

The general products page is rendered by the Products component (Products.jsx), which displays a search bar and a list of products. Here’s a brief explanation of the Products component:

  • Lines 35–43: We define the header parameters and options object to make GET requests with a JSON response.

  • Lines 46–59: We declare the query parameters and URL for the search products by query string endpoint, which we call using our custom useFetch hook.

A specific recipes page is rendered by the RecipePage component (RecipePage.jsx), which displays details about the recipe and the recipe widgets. Here’s a brief explanation of the RecipePage component:

  • Lines 76–84: We define the header parameters and options object to make GET requests with a JSON response.

  • Lines 85–93: We define the header parameters and options object to make GET requests with an HTML response.

  • Lines 96–103: We declare the query parameters and URL for the recipe information endpoint, which we call using our custom useFetch hook.

  • Lines 106–117: We declare the query parameters and URL for the similar recipes endpoint, which we call using our custom useFetch hook.

  • Lines 120–131: We declare the query parameters and URL for the recipe nutrition by ID widget endpoint, which we call using our custom useFetch hook. Using a dropdown menu, we can change this URL to automatically fetch the HTML data for other recipe widgets and display them.

A specific ingredient page is rendered by the IngredientPage component (IngredientPage.jsx), which displays details about the ingredient and suggests any product with the specified ingredient in it. Here’s a brief explanation of the IngredientPage component:

  • Lines 30–38: We define the header parameters and options object to make GET requests with a JSON response.

  • Lines 41–50: We declare the URL for the ingredient information endpoint, which we call using our custom useFetch hook.

  • Lines 53–63: We declare the query parameters and URL for the ingredient substitute by ID endpoint, which we call using our custom useFetch hook.

The ProductSuggestions component (ProductSuggestions.jsx), a child component of the IngredientPage, displays product suggestions on a specific ingredient page rather than a separate page. Here’s a brief explanation of the ProductSuggestions component:

  • Lines 12–27: We define the header parameters, body parameters, and options object to make POST requests with a JSON response.

  • Lines 28–36: We declare the URL for the search products by ingredients endpoint, which we call using our custom useFetch hook.

The ProductLoadingCard component (ProductLoadingCard.jsx), a child component of ProductSuggestions, displays a detailed product card after fetching the associated product’s information. Here’s a brief explanation of the ProductLoadingCard component:

  • Lines 17–25: We define the header parameters, body parameter, and options object to make POST requests with a JSON response.

  • Lines 28–36: We declare the URL for the product information endpoint, which we call using our custom useFetch hook.

A specific product page is rendered by the ProductPage component (ProductPage.jsx), which displays details about the product and the product widgets. Here’s a brief explanation of the ProductPage component:

  • Lines 44–52: We define the header parameters and options object to make GET requests with a JSON response.

  • Lines 53–61: We define the header parameters and options object to make GET requests with an HTML response.

  • Lines 64–72: We declare the URL for the product information endpoint, which we call using our custom useFetch hook.

  • Lines 75–86: We declare the query parameters and URL for the product nutrition by ID widget endpoint, which we call using our custom useFetch hook. Using a dropdown menu, we can change this URL to automatically fetch the HTML data for other product widgets and display them.