Purpose
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.
Codesandbox
The completed project is available on Codesandbox.
You can find the full codebase on my Github
App Setup
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:
1yarn create nuxt-app nuxtjs-movie-posters2# OR3npx create-nuxt-app nuxtjs-movie-posters4# OR5npm init nuxt-app nuxtjs-movie-posters
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.
1cd nuxtjs-movie-posters23yarn dev4# OR5npm run dev
Your app will now be running on localhost:3000.
Cloudinary setup
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.
1touch .env
The .env
file will house our environmental variables. These are values we do not want to be stored in our codebase.
1<!-- .env -->2NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloudinary-cloud-name>
We can now install the recommended Nuxt.Js plugin.
1yarn add @nuxtjs/cloudinary2# OR3npm install @nuxtjs/cloudinary
Add it as a module in the modules
section of the nuxt.config.js
file.
1// nuxt.config.js2export default {3 ...4 modules: [5 '@nuxtjs/cloudinary'6 ]7 ...8}
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
.
1export default {2 ...3 cloudinary: {4 cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,5 useComponent: true6 }7}
You are now ready to use Cloudinary in your project.
TMDB Setup
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.
1<!-- .env -->2NUXT_ENV_TMDB_API_KEY=<your-tmdb-api-key>
Searching for the movie
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.
1<!-- pages/index.vue -->2<templete>3 ...4 <form @submit.prevent="search">5 <div>6 <label for="search">Search</label>7 <div>8 <input9 v-model="searchQuery"10 type="text"11 name="search"12 id="search"13 placeholder="Search for movie..."14 />15 </div>16 </div>17 <div>18 <button19 v-if="!loading"20 type="submit"21 >22 {{searchQuery ? "Search" : "Random"}}23 </button>24 <p v-if="loading">25 {{loading}}26 </p>27 </div>28 </form>29 ...30</template>
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.
1// pages/index.vue2<script>3export default {4 data(){5 return {6 loading:false,7 searchQuery: null,8 show:null,9 };10 },11 mounted(){12 this.search();13 },14 methods:{15 search(){16 Array.prototype.random = function () {17 return this[Math.floor((Math.random()*this.length))];18 }1920 this.loading = "Getting search results...";21 const requestOptions ={22 method: 'GET',23 redirect: 'follow'24 };2526 const requestURL = "q?";2728 const alphabet = "abcdefghijklmnopqrstuvwxyz";2930 const queryParams = new URLSearchParams({31 api_key: process.env.NUXT_ENV_TMDB_API_KEY,32 query: this.searchQuery ?? [...alphabet].random(),33 });3435 fetch(requestURL + queryParams, requestOptions)36 .then(response => response.json())37 .then(38 response => this.show = response39 .results40 .filter(show => show.backdrop_path)41 .random()42 )43 .then(() => this.loading="Loading image..")44 .catch(error => alert('Error:' + error));45 },46 }47}48</script>
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...".
Generating the poster
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.
1// pages/index.vue2export default{3 ...4 computed:{5 posterURL(){6 if(!this.show){7 return;8 }9 return this.$cloudinary.image.fetchRemote(10 `https://image.tmdb.org/t/p/original/${this.show.backdrop_path}`,11 {12 width:800,13 height:800,14 crop:"fit",15 transformation: [16 {17 color:"#" + Math.floor(Math.random()*16777215).toString(16),18 overlay: {19 font_family: "Ultra",20 font_weight: "bold",21 font_size: 120,22 text: this.show.title23 }24 },25 {flags: "layer_apply", gravity: "south", y: 100}26 ]27 }28 );29 }30 },31 ...32}
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.
1<!-- pages/index.vue -->2<template>3 ...4 <div>5 <img6 :alt="`${show.title} poster`"7 v-if="show"8 :src="posterURL"9 referrerpolicy="no-referrer"10 @load="loading = null"11 @error="search"12 />13 </div>14 ...15</template>
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:
Resources
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.