Skip to Content
Course content

221: Building a Recipe Finder App Using a Public API

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this happen countless times with developers moving from local logic to real-world apps: they treat an API call like a standard JavaScript function. They think that if they call fetch(), the data just... appears in their variable, ready to be used. It's a frustrating wall to hit because the code looks logically sound, but the output is always Promise {<pending>}.

The myth: "fetch() returns my data"

Imagine you're building our Recipe Finder. You want to get a list of chicken recipes from TheMealDB API. You might write something like this:

const recipes = fetch('https://www.themealdb.com/api/json/v1/1/filter.php?i=chicken_breast');
console.log(recipes); // You expect a list of recipes, but you get a Promise object.

The reason this fails is that the internet is slow (relative to your CPU). JavaScript doesn't stop the entire browser from freezing while it waits for a server in another country to respond. Instead, fetch returns a Promise—a placeholder that says, "I'll let you know when the data actually gets here." If you try to map over recipes immediately, your app will crash because you can't iterate over a Promise.

The reality: The two-step resolution process

To actually get your recipes, you have to await the response, and then—this is the part people often forget—you have to tell JavaScript how to read that response. The raw response from a server is just an HTTP stream; it isn't a JavaScript object yet. You have to parse it as JSON.

async function getRecipes(ingredient) {
  const response = await fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${ingredient}`);
  
  // This second 'await' is where the actual data extraction happens
  const data = await response.json(); 
  
  return data.meals; // TheMealDB wraps results in a 'meals' array
}

I always tell my mentees to think of it like ordering a pizza. fetch is the act of placing the order. The Response object is the delivery driver arriving at your door. But you can't eat the pizza while it's still inside the cardboard box—.json() is the act of opening the box so you can actually access the food.

Connecting the API to your UI

Now that we have the data, we can't just dump a JSON array into the HTML. We need to transform that data into something a human wants to see. In a Recipe Finder, this usually means looping through the array and generating "recipe cards."

I prefer using .map() combined with .join('') to create a big string of HTML. It's much cleaner than appending elements one by one in a forEach loop.

async function displayRecipes() {
  const searchInput = document.querySelector('#search-box').value;
  const recipes = await getRecipes(searchInput);
  const container = document.querySelector('#recipe-grid');

  if (!recipes) {
    container.innerHTML = '<p>No recipes found for that ingredient!</p>';
    return;
  }

  container.innerHTML = recipes.map(meal => `
    <div class="recipe-card">
      <img src="${meal.strMealThumb}" alt="${meal.strMeal}">
      <h3>${meal.strMeal}</h3>
      <a href="recipe-details.html?id=${meal.idMeal}">View Recipe</a>
    </div>
  `).join('');
}

Dealing with the "Empty State"

One detail that separates a junior project from a professional app is how it handles failure. What happens if the user searches for "unobtainium"? The API won't throw an error; it will simply return an object where meals is null.

If you try to call .map() on null, your entire app will white-screen. I've included a simple if (!recipes) check in the code above. Never trust the API to always give you a valid array. Always verify the data exists before you try to render it to the DOM.




📋 Practical Task

Implementing a "Random Recipe" Discovery Button

Your Recipe Finder currently relies on user input, but users often don't know what they want. Your task is to add a "Surprise Me" feature.

Requirements:

  • Add a button to your HTML with the ID #random-btn.
  • Create a function that fetches a single random meal from this endpoint: https://www.themealdb.com/api/json/v1/1/random.php.
  • When the button is clicked, clear the current recipe grid and display only that one random recipe card.
  • Ensure you handle the asynchronous nature of the request using async/await.
  • Bonus: Add a loading spinner or a "Searching..." text to the grid while the random recipe is being fetched.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.