Mastering API Fetch: How to Build a Movie Website with Real-Time Data Updates
Tags: webdev, beginners, api, javascript
Published: 2023-06-02 21:58:33

**Learn how to use the Fetch API to asynchronously request data from an external API and dynamically build a movie website. This beginner-friendly tutorial is great for learning about APIs, async JS, and building dynamic front-end applications.**

This article will explore how API fetch works while building a movie website.

## What Is Fetch?
Fetch is a web API provided by modern browsers that allow developers to make HTTP requests to servers and retrieve data or resources from them.

If you have worked with XMLHttpRequest (XHR) object, the Fetch API can perform all the tasks as the XHR object does.

## Using the Fetch API
To use Fetch, developers can call the global `fetch()` function and pass in a URL to the resource they want to fetch.


```
fetch('https://apiexample.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));
```

- The above code retrieves JSON data from a URL called **https://apiexample.com/data** and parses it as JSON using the .json() method, If an error occurs during the fetch, the catch() method logs an error message to the console.

## Features of the movie website
Using TMDB API to display movie posters and information on the movie card.

Building a functional search bar to search for any movie and display results.

A modal box that displays details of the movie when you click on the movie card.

## The layout of the website

- The website would have a header, main, and footer.
- The Header consists of the movie title and a search bar to search for movies.
- The Search Bar
- Main - This is where the movie card will be displayed.
- Footer - The footer will carry copyright text along with the name of the developer.

## The Header
Copy and paste this code into your code editor.


```
<header class="header">
        <div class="movieheader">
            <p style="display: flex; align-items: center;">🎥any<span style="color: hsla(349, 69%, 51%, 1);">movie</span></p>
        </div>

        <form action="/" class="search-btn">
            <input type="text" placeholder="search for a movie...">
        </form>
    </header>
```
The HTML tag consists of the movie title with the class name "movie header" and a search bar `"search-btn"`.

The input field for the search bar is wrapped up in a form container because it provides a way to submit data to a server for processing. When a user enters a movie query into the search bar and hits `"Enter"`, the data entered into the input field is packaged into an HTTP request and sent to a server for processing.


```
*,
*::before,
*::after{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body{
    font-family: 'Ubuntu', sans-serif;
    background-color: rgb(11, 12, 13);
}

.movieheader{
    font-size: 1.2rem;
    color: hsla(200, 100%, 95%, 1);
}
.movieheader p{
    font-size: 1.2rem;
    font-weight: 600;
}

.header{
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 95%;
    margin: 1.4rem auto;
}
.search-btn{
    align-items: center;
    border-radius: 5px;
}
.search-btn input{
    outline: none;
    border: none;
    padding: .5rem .3rem;
    font-size: .9rem;
    background-color: hsla(250, 6%, 20%, 1);
    color: hsla(200, 100%, 95%, 1);
    border-radius: 5px;
}


@media screen and (min-width: 750px) {
    .header{
        width: 90%;
        padding: 1rem 0;
    }
    .movieheader p{
        font-size: 1.7rem;
    }
    .search-btn input{
        width: 50vh;
    }
    .sm-search{
        display: none;
    }
    .search-btn input{
        padding: .8rem .5rem;
        font-size: 1rem;
    }
}



@media screen and (min-width: 1024px) {
    .header{
        width: 90%;
        padding: 1rem 0;
    }
    .movieheader p{
        font-size: 1.7rem;
    }
    .search-btn input{
        width: 50vh;
    }
    .sm-search{
        display: none;
    }
    .search-btn input{
        padding: .8rem .5rem;
        font-size: 1rem;
    }
}
```

- It has a dark background color of `rgb(11, 12, 13)`. On the Desktop screen, the font size of the movie title is `font-size: 1.7rem;` and on a mobile screen, the font size is `1.2rem`.
- I added a media query for responsiveness.

## The Search Bar Functionality

```
<form action="/" class="search-btn">
   <input type="text" placeholder="search for a movie...">
</form>
```

- I used the `<form>` tag as a container for the search bar input field because it provides a way to submit the search query to a server for processing.


```
const form = document.querySelector('.search-btn');
const movieCon = document.querySelector('.movieContainer');
const input = form.querySelector('input');
const searchResults = document.querySelector('.search-result');
const searchResultContainer = document.querySelector('.searchResultContainer');


form.addEventListener('submit', async (event) => {
  event.preventDefault();
  const query = input.value;
  const apiKey = 'YOUR_API_KEY'; // replace with your actual API key
  const apiUrl = `https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&query=${query}`;

  try {
    const response = await fetch(apiUrl);
    const data = await response.json();
    const movies = data.results;

    searchResults.innerHTML = '';
    movieCon.innerHTML = '';


    movies.forEach((movie) => {
      const { poster_path, title, release_date, vote_average } = movie;

      const movieContainer = document.createElement('div');
      movieContainer.classList.add('movieCard');

      const posterUrl = poster_path
        ? `https://image.tmdb.org/t/p/w500/${poster_path}`
        : 'https://via.placeholder.com/500x750?text=No+poster';

      movieContainer.innerHTML = `
        <img src="${posterUrl}" alt="${title}" class="moviePoster">
        <div class="movieTitle">${title}</div>
        <div class="rating">
          <span class="rate">${vote_average}</span>
          <span class="year">${release_date ? release_date.slice(0, 4) : ''}</span>
        </div>
      `;

      searchResults.appendChild(movieContainer);
    });

    const header = searchResultContainer.querySelector('header');
    if (!header) {
      const header = document.createElement('header');
      header.innerHTML = `<h1>Search Results: ${query}</h1>`;
      searchResultContainer.insertBefore(header, searchResults);
    } else {
      header.innerHTML = `<h1>Search Results: ${query}</h1>`;
    }
  } catch (error) {
    console.error(error);
  }

});
```

- This above code sets up a search feature for movies using The Movie Database TMDB API. It starts by selecting relevant elements from the HTML document using their class names (`form`, `movieCon`, `input`, `searchResults`, and `searchResultContainer`).
- The code then adds an event listener to the form element for the `submit` event. When the user submits the form, the event listener function is called. The function first prevents the default form submission behavior with `event.preventDefault()`.
- It then retrieves the value of the user's search query from the input element and constructs the API endpoint URL with the query and the TMDB API key.
- The code then sends a request to the TMDB API using `fetch()`. When the response is received, it is converted from JSON format to a JavaScript object using `response.json()`. The object is then processed to extract an array of movies matching the search query.
- The movies array is then looped over using `forEach()`, and for each movie, a `div` element with the movie details is created using `document.createElement()`. The `div` element is then populated with movie data and appended to the `searchResults` element.
- The code then checks if there is already a header element in the `searchResultContainer`. If there isn't, a new header element is created and inserted before the `searchResults` element. If there is, the `h1` element of the header is updated with the new search query.
- If any errors occur during the API request and response process, they are logged to the console using `console.error()`.

## Displaying the Movies - The Main
I will be using TMDB API to display movies on the website. Before that, create a movie container in the HTML body for the movie card and details.


```
<div class="movieContainer">
        <div class="movieCard">
            <img src="" alt="" class="moviePoster">
            <div class="movieTitle">John Wick</div>

            <div class="rating">
                <span class="rate"></span>
                <span class="year"></span>
            </div>
        </div>
    </div>
```
**NOTE: If you don't have an API Key, you would be required to sign up on the TMDB website.**

**Styling The Movie Container**


```
/*Movie container for mobile screen*/
.movieContainer{
    display: flex;
    justify-content: center;
    flex-direction: column;
    width: 90%;
    margin: auto;
    margin-bottom: 5rem;
    padding-bottom: 2rem;
}
.movieCard{
    margin: .5rem 0;
}

.moviePoster{
    width: 100%;
    height: 40vh;
    object-fit: cover;
}

.movieTitle{
    font-size: 1rem;
    font-weight: 600;
    margin: .6rem 0;
}

.rating{
    display: flex;
    justify-content: space-between;
    color: gray;
    font-weight: 600;
}

@media screen and (min-width: 1024px) {
.movieContainer{
    flex-direction: row;
    flex-wrap: wrap;
}

.movieCard{
    margin: .6rem;
    width: 200px;
}
```

- On mobile screen. the flex-direction of the movie container is set to `column`. So it displays one movie per column. While on the Desktop, it displays 6 movie cards per row.


**Javascript Functionality For API Call To Display Movies**


```
const apiKey = 'YOUR_API_KEY';

fetch(`https://api.themoviedb.org/3/movie/popular?api_key=${apiKey}`)
  .then(response => response.json())
  .then(data => {
    console.log(data)
    const movies = data.results;
    const movieContainer = document.querySelector('.movieContainer');

    movies.forEach(movie => {
      const movieCard = document.createElement('div');
      movieCard.classList.add('movieCard');

      const moviePoster = document.createElement('img');
      moviePoster.classList.add('moviePoster');
      moviePoster.src = `https://image.tmdb.org/t/p/w500${movie.poster_path}`;
      moviePoster.alt = movie.title;

      const movieTitle = document.createElement('div');
      movieTitle.classList.add('movieTitle');
      movieTitle.innerText = movie.title;

      const rating = document.createElement('div');
      rating.classList.add('rating');

      const rate = document.createElement('span');
      rate.classList.add('rate');
      rate.innerText = `${movie.vote_average}/10`;

      const year = document.createElement('span');
      year.classList.add('year');
      year.innerText = movie.release_date.split('-')[0];

      rating.appendChild(rate);
      rating.appendChild(year);

      movieCard.appendChild(moviePoster);
      movieCard.appendChild(movieTitle);
      movieCard.appendChild(rating);

      movieContainer.appendChild(movieCard);
    });
  })
  .catch(error => {
    console.error('Error fetching movies:', error);
  });
```
The above code will display popular movies including movie images, title, rating, and year of release. Here is how I went about it:

- The line defines a variable to store your API Key.

```
const apiKey = 'YOUR_API_KEY';
```

- The fetch function calls the URL of the API endpoint as a parameter, with the `apiKey` variable inserted into the URL string using template literals - `{apiKey}`.
- The fetch function returns a Promise, which is resolved with the response data obtained from the API. The response is then parsed as JSON using the `json()` method, which also returns a Promise.

  

```
fetch(`https://api.themoviedb.org/3/movie/popular?api_key=${apiKey}`)
    .then(response => response.json())
    .then(data =>
```

- The container element for the movies is obtained using `document.querySelector()`, and then a loop is used to iterate over each movie in the list.


```
const movieContainer = document.querySelector('.movieContainer');
```

- For each movie, a new `div` element is created using `document.createElement()`, with a class of `movieCard`. An `img` element is also created for the movie poster, with a class of `moviePoster` and an alt attribute set to the movie title.

```
movies.forEach(movie => {
      const movieCard = document.createElement('div');
      movieCard.classList.add('movieCard');

      const moviePoster = document.createElement('img');
      moviePoster.classList.add('moviePoster');
      moviePoster.src = `https://image.tmdb.org/t/p/w500${movie.poster_path}`;
      moviePoster.alt = movie.title;
}
```
Two additional elements are created for the movie title and its rating, with classes of `movieTitle` and `rating`, respectively. The rating element contains two child `span` elements for the rating and year of release, with classes of rate and year, respectively. The movie `poster`, `title`, and `rating` elements are then added as child elements of the `movieCard` div. Finally, the `movieCard` div is added as a child element of the movie container element obtained earlier.


```
const movieTitle = document.createElement('div');
      movieTitle.classList.add('movieTitle');
      movieTitle.innerText = movie.title;

      const rating = document.createElement('div');
      rating.classList.add('rating');

      const rate = document.createElement('span');
      rate.classList.add('rate');
      rate.innerText = `${movie.vote_average}/10`;

      const year = document.createElement('span');
      year.classList.add('year');
      year.innerText = movie.release_date.split('-')[0];

      rating.appendChild(rate);
      rating.appendChild(year);

      movieCard.appendChild(moviePoster);
      movieCard.appendChild(movieTitle);
      movieCard.appendChild(rating);

      movieContainer.appendChild(movieCard);
    });
  })
```

## Building A Box Modal To display Movie details
**I thought about creating a modal box to display the movie details, what is your thought about doing in another article? Let me know what you think about that.
**
If you have a question, do let me know in the comment section. Also like and follow me if you found this article helpful.

## The Footer

```
<footer>
   <div class="footerContainer">
    <p>Copyright 2023</p>
    <p>Created By Tracy</p>            
    </div>     
</footer>
```

- The `footer` tag contains two paragraph tags stating the name of the developer and a copyright text.

```
footer{
    background-color: #A01D1E;
    font-weight: 600;
    margin-top: 1rem;
    padding: 1rem 1.1rem;
}
.footerContainer{
    display: flex;
    justify-content: space-between;
}
```

@media screen and (min-width: 1024px){
    .footerContainer{
    justify-content: space-around;
}
}

- On a mobile screen, the spacing of the paragraph tag is set to space-between, while on a desktop, it is set to space-around.

## The Final Result
[The final result on Codepen](https://codepen.io/Tracy4work/pen/vYVqowB)