Node.js movie app - Receive data from the API
Part 1: Creating the framework
Our app is equipped with the tools which write data to the console in various colours and it can also display our responses by prompting us to answer pre-defined questions.
The next step is to connect to the movie database and receive data according to the responses.
The API
First, we need a database where we can fetch movie data from.
Several good movie databases exist out there and we’ll use the OMDb API.
Getting the API key. The database is free to use but you will need an API key to access its content. Simply click on the link and go to the API Key
tab. Choose the free option and enter your email. The API key will be in your mailbox within a few seconds. I haven’t received any spams or unsolicited emails since I subscribed and the API is really good!
Using the API. The API is very easy to use. You will obviously need your key to access the database and have to include either the id or the title of the movie or, you can make a general search on the movies, too.
We’ll use both approaches in the app, but before fetching the first movie, we’ll need to install a cool library that helps us connecting to APIs and receiving data from them.
Fetching data
One of these libraries is request-promise-native.
Installing request
request-promise-native
supports the classic CRUD methods, and therefore it’s relevant to our purposes of fetching data. As its name suggests it returns the response wrapped in a Promise and uses the native ES6 promises.
Installation. The library depends on the original module called request
, so we have to install both.
If you are not in the project folder, navigate to it and type npm install --save request request-promise-native
.
Adding a request
Creating a new file. We have started to write features in separate modules by extracting prompts to their own file to keep index.js
clean. Let’s continue this by creating a requests.js
file in the modules
library, so type touch modules/requests.js
in the terminal.
Open the newly created requests.js
file and require request-promise-native
at the top of the file:
const rp = require('request-promise-native');
Adding the API key. The query to the database must contain a valid API key we received from the maintainer, and we’ll have to include it into the URL.
We won’t include the API in the code in this application. So how can it be added to the URL?
One way to add the key is by using an environment variable.
Environment variables. Environment variables are useful concepts in Node.js. A frequent use case of them is to separate development, test and production environments, but in this case I use them because I don’t want to share my API key with the world. They are only valid for the time the process runs, which means that we have to provide it each time when we run application.
The first request. This means that our request function should contain the API key as a parameter.
The function also has to work with other parameters to receive a useable response from the API.
The first question we ask of the users of our fantastic app is on the title of the movie, and then we prompt them to narrow their search by defining the type (movie, series or episode) of the movie.
We’ll receive the title (or a part of it) and the type in the response object and we’ll also add them as parameters of our request function, which can look like this:
const getMovieDataFromDB = (movieTitle, movieType, apiKey) => {
const options = {
method: 'GET',
uri: `https://www.omdbapi.com/?s=${ movieTitle }&type=${ movieType }&apikey=${ apiKey }`,
json: true
};
return rp(options);
};
getMovieDataFromDB
gives back a Promise
returned by request-promise-native
. We need to feed in some options though.
options
object contains few options from the many available. We want to GET
data from the database, so we need to specify this in the method
property. We’ll also have to declare the uri
where the data is fetched from and we want the data in a form of a JSON
object.
Export it. The user responses are already available in index.js
as movieData
, so we’ll need to call getMovieDataFromDB
here. In order to do this, we have to export the function:
module.exports = { getMovieDataFromDB }
Require it. As such, we have to require
it in index.js
at the top of the file:
const { getMovieDataFromDB } = require('./modules/requests');
Getting real data
Now that we have getMovieDataFromDB
available in index.js
, we can feed in the responses the user gave us.
We know that Inquirer.js saves the answers in an object with properties of movieTitle
and movieType
. We can extract these properties from the returned object using object destructuring.
In index.js
, replace the movieData
inside the try
block with the following code:
const { movieTitle, movieType } = await getMovieTitleFromUser();
From now we can use the user responses as movieTitle
and movieType
when making a request to the API:
const movieDataFromDB = await getMovieDataFromDB(movieTitle, movieType);
We call getMovieDataFromDB
here with the user’s responses and because the function returns a promise, we await
the response. Let’s save the result in the movieDataFromDB
variable.
Adding the API key. If you run the app now and try to make a search on a movie, you will get a friendly error message that the API key is invalid. Because this is not far from the truth, let’s add the API key to the list of environment variables as discussed above.
Environment variables can be accessed as a property of the process.env
object. Let’s create an apiKey
variable outside the vorpal
call:
// ...
figlet('Movies', (_, data) => {
console.log(chalk.cyan(data));
const apiKey = process.env.API_KEY;
Environment variables are named in upper case by convention and ours will surprisingly called API_KEY
(it can be named to anything expressive).
Let’s add apiKey
to getMovieDataFromDB
as the third argument:
const movieDataFromDB = await getMovieDataFromDB(movieTitle, movieType, apiKey);
Run the script. We can now start the script again, but this time with API_KEY
provided. First, specify the name of the environment variable (API_KEY
) and then add the key you received in email.
API_KEY=yourapikey npm start
If you run the following command, then type search, enter the title of the movie and specify the type, you should receive an object with one or more movies matching the title and type you entered.
For example, upon entering Jason Bourne
and selecting Movie
, we’ll receive quite a few objects similar to this:
{
"Title": "Jason Bourne",
"Year": "2016",
"imdbID": "tt4196776",
"Type": "movie",
"Poster": "https://m.media-amazon.com/images/M/MV5BNGJlYjVkMjQtN2NlZC00NTJhLThmZjItMTRlZDczMmE3YmI3XkEyXkFqcGdeQXVyMzI0NDc4ODY@._V1_SX300.jpg"
}
The script is working!
Adding API error message
We declared the apiKey
outside the vorpal
body because we might not want to start the script if the API key is not entered.
We can therefore add a quick check if the API key is provided right under the apiKey
variable declaration:
if (!apiKey) {
console.log(chalk.red('Please enter your API key!'));
return;
}
It’s fairly straightforward; if the apiKey
doesn’t exist, we display a gentle message and simply return
, so the script won’t start.
Invalid API keys are handled by the API itself and those issues will directed to the catch
block.
The modified and new files should look like this:
// index.js
const chalk = require('chalk');
const figlet = require('figlet');
const vorpal = require('vorpal')();
const { getMovieDataFromDB } = require('./modules/requests');
const { getMovieTitleFromUser } = require('./modules/user-inputs');
figlet('Movies', (_, data) => {
console.log(chalk.cyan(data));
const apiKey = process.env.API_KEY;
if (!apiKey) {
console.log(chalk.red('Please enter your API key!'));
return;
}
vorpal
.command('search', 'Start searching movies')
.action(async () => {
try {
const { movieTitle, movieType } = await getMovieTitleFromUser();
const movieDataFromDB = await getMovieDataFromDB(movieTitle, movieType, apiKey);
vorpal.log(chalk.green(JSON.stringify(movieDataFromDB, null, 2)));
} catch (error) {
vorpal.log(chalk.red(error));
}
});
vorpal
.delimiter('Movies$')
.show();
});
and
// requests.js
const rp = require('request-promise-native');
const getMovieDataFromDB = (movieTitle, movieType, apiKey) => {
const options = {
method: 'GET',
uri: `https://www.omdbapi.com/?s=${ movieTitle }&type=${ movieType }&apikey=${ apiKey }`,
json: true
};
return rp(options);
};
module.exports = { getMovieDataFromDB }
We haven’t touched user-inputs.js
this time, so it remained the same as it was in Part 2.
Conclusion
The app can now receive real data from the movie database and it’s really cool!
In the next part we’ll select the movie we like from those the database gave us and will receive more detailed information on the selected movie.
Thanks for reading and see you next time.