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 isfont-size: 1.7rem;
and on a mobile screen, the font size is1.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
, andsearchResultContainer
). - 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 withevent.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 usingresponse.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, adiv
element with the movie details is created usingdocument.createElement()
. Thediv
element is then populated with movie data and appended to thesearchResults
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 thesearchResults
element. If there is, theh1
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 usingdocument.createElement()
, with a class ofmovieCard
. Animg
element is also created for the movie poster, with a class ofmoviePoster
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.
Top comments (13)
As far as I know - maybe I'm wrong - fetch does not throw like axios or got lib on http errors like 404.
There is a
result.ok
which can/should be used to evaluate that the response is some 200 and not some 404 or 500. And in case of an error, you can simply useresponse.json()
to get the error response sent from server - much easier than with axiosSo the try-catch-block might not work as you expected here.
Thanks for the heads up. I will keep that in mind.
Yeah, it’s a bit unexpected here, because it follows more the Rust language response handling and not the typical JavaScript-simply-throw pattern.
I only mentioned it, because I was doing it wrong, lost quite some time on it - one or two weeks ago 🤫😂
Well, it is not something I am familiar with so I had to check it out. It actually works so thanks.
Discover the ultimate Goku experience with our Goku to Movies app! Immerse yourself in the world of epic battles and heroic deeds as you stream your favorite Goku movies and episodes. Download now to join Goku on his legendary quest and unleash the power of the Saiyan warrior within!
It's a bit surprising here, as it aligns more with Rust's response handling rather than the typical JavaScript 'simply throw' pattern. I bring it up because I recently encountered this myself, spending quite some time on it about a week or two ago.
Regards: sonic happy hour
I was not familiar with this either, so I looked into it. It does work thanks for the tip Interestingly I found that it also enhances overall performance if used consistently.
Regards: ff advance server
Great tutorial for beginners! Explains Fetch API and dynamic website creation well. To add, tamildhool app apk download free offers a similar concept for Indian movie lovers, providing real-time updates and a user-friendly interface for watching Tamil movies on the go.
Thanks for sharing, I will definitely try this on Goku.to Movies.
for you favourtie action games regarding free fire, you must join free fire advance server which has spanish version servidor avanzado free fire
Stream your favorite movies anytime, anywhere with Goku.tu Movies App your ultimate destination for cinematic delights.
Unlock a world of cinematic wonders with Goku.tu App, boasting an extensive library of films across genres.
you have given a great app. i loved to watch movies on your app. here is another application which is use to play game that is servidor avanzado ff