Consuming APIs and generating custom images from the data received is a wholesome task. Let us explore how we can generate dynamic movie posters for films of the TMDB database.
The completed project is available on Codesandbox.
You can find the full codebase on my Github
For this article, we will be using Nuxt.Js, an amazing Vue.Js framework. To get started, ensure you have Yarn or NPM v5.2+ or v6.1+ installed. Open your preferred working directory and run the following command:
yarn create nuxt-app nuxtjs-movie-posters
# OR
npx create-nuxt-app nuxtjs-movie-posters
# OR
npm init nuxt-app nuxtjs-movie-posters
Code language: PHP (php)
This will result in a series of various setup questions. Here are our recommended defaults:
Once the setup is complete, you may enter and run your app.
cd nuxtjs-movie-posters
yarn dev
# OR
npm run dev
Code language: PHP (php)
Your app will now be running on localhost:3000.
We will be using cloudinary‘s powerful Nuxt.Js SDK to manipulate our images. Login to your Cloudinary console and copy your cloud-name
. If you do not have an account, feel free to create one here. We are going to add our cloud-name
to our .env
file. Let’s create the file.
touch .env
Code language: CSS (css)
The .env
file will house our environmental variables. These are values we do not want to be stored in our codebase.
<!-- .env -->
NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloudinary-cloud-name>
Code language: HTML, XML (xml)
We can now install the recommended Nuxt.Js plugin.
yarn add @nuxtjs/cloudinary
# OR
npm install @nuxtjs/cloudinary
Code language: CSS (css)
Add it as a module in the modules
section of the nuxt.config.js
file.
// nuxt.config.js
export default {
...
modules: [
'@nuxtjs/cloudinary'
]
...
}
Code language: JavaScript (javascript)
Add a cloudinary
section to configure the plugin. In this section, we will be linking it to our Cloudinary account by registering our cloud-name
.
export default {
...
cloudinary: {
cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
useComponent: true
}
}
Code language: JavaScript (javascript)
You are now ready to use Cloudinary in your project.
We will be getting the movie details from TMDB (The Movie Database). First, create an account here if you do not have one. Once registered and logged in, proceed to the API setting section to obtain your API key. You will have to answer a few questions with regards to your API consumption.
We will now add the API key to our .env
file.
<!-- .env -->
NUXT_ENV_TMDB_API_KEY=<your-tmdb-api-key>
Code language: HTML, XML (xml)
First and foremost, we want our users to either search for a film of their choice or obtain a random film. Let us add a basic HTML form for this purpose.
<!-- pages/index.vue -->
<templete>
...
<form @submit.prevent="search">
<div>
<label for="search">Search</label>
<div>
<input
v-model="searchQuery"
type="text"
name="search"
id="search"
placeholder="Search for movie..."
/>
</div>
</div>
<div>
<button
v-if="!loading"
type="submit"
>
{{searchQuery ? "Search" : "Random"}}
</button>
<p v-if="loading">
{{loading}}
</p>
</div>
</form>
...
</template>
Code language: HTML, XML (xml)
The above form accepts a search input and triggers the search
method when submitted. When there is no search input, the button reads Random
but submits to the same method. Let us now prepare the corresponding JavaScript.
// pages/index.vue
<script>
export default {
data(){
return {
loading:false,
searchQuery: null,
show:null,
};
},
mounted(){
this.search();
},
methods:{
search(){
Array.prototype.random = function () {
return this[Math.floor((Math.random()*this.length))];
}
this.loading = "Getting search results...";
const requestOptions ={
method: 'GET',
redirect: 'follow'
};
const requestURL = "q?";
const alphabet = "abcdefghijklmnopqrstuvwxyz";
const queryParams = new URLSearchParams({
api_key: process.env.NUXT_ENV_TMDB_API_KEY,
query: this.searchQuery ?? [...alphabet].random(),
});
fetch(requestURL + queryParams, requestOptions)
.then(response => response.json())
.then(
response => this.show = response
.results
.filter(show => show.backdrop_path)
.random()
)
.then(() => this.loading="Loading image..")
.catch(error => alert('Error:' + error));
},
}
}
</script>
Code language: HTML, XML (xml)
The above search
method first declares a method to get random elements from arrays. We then proceed to query the https://api.themoviedb.org/3/search/movie
route with the search query and our API key. If there is no search string, a random letter is used as the search query. Once the request is successful, the results are filtered to remove those without a backdrop_path
. This is the path that contains the image we will use for the poster. A random show from the remaining is assigned to the show
variable in the state and the loading
indicator is changed to “Loading image…” from “Getting search results…”.
To generate the poster, we will utilize the globally injected $cloudinary
instance. If there is no show, return nothing. Otherwise, fetch the image from the remote source.
Once the image is fetched we overlay the title with the random text color. We place the title at the bottom of the poster which is the norm with movie posters.
// pages/index.vue
export default{
...
computed:{
posterURL(){
if(!this.show){
return;
}
return this.$cloudinary.image.fetchRemote(
`https://image.tmdb.org/t/p/original/${this.show.backdrop_path}`,
{
width:800,
height:800,
crop:"fit",
transformation: [
{
color:"#" + Math.floor(Math.random()*16777215).toString(16),
overlay: {
font_family: "Ultra",
font_weight: "bold",
font_size: 120,
text: this.show.title
}
},
{flags: "layer_apply", gravity: "south", y: 100}
]
}
);
}
},
...
}
Code language: JavaScript (javascript)
We customize the title using the Ultra
font, boldness and 120
as the font size. Now that our URL is ready, let us display it.
<!-- pages/index.vue -->
<template>
...
<div>
<img
:alt="`${show.title} poster`"
v-if="show"
:src="posterURL"
referrerpolicy="no-referrer"
@load="loading = null"
@error="search"
/>
</div>
...
</template>
Code language: HTML, XML (xml)
The above HTML code displays the poster when show
is not empty or negative. The no-referrer
policy enables us to obtain the image from our app as the image is restricted to direct browser visits only.
Once the image is loaded, we remove the loading indicator. However, if the image loading fails, we load a different image by triggering the search all over again.
When it is all done, we should have an output similar to the following:
To learn more about how to interact with the TMDP API, feel free to browse their documentation.
You may review the Cloudinary package documentation to learn more about its capabilities as well as review the official Cloudinary docs.