CLI JS App Using Google Books API

Olesya Miller
3 min readJun 15, 2021

Hi guys! I had to build a small CLI JS project for one of my job applications. It came out pretty cool and I learn a few new things. Today I’m going to share it with you.

The assignment was to create a command line application that allows you to use the Google Books API to search for books and construct a reading list.

The first thing you have to do is to get your API key from Google. Once you have it you can start making API requests using different parameters.

To be able to make fetch requests you have to install node-fetch package. After installing it require it at the top of your file like this

const fetch = require('node-fetch');

Since this is going to be a CLI app we somehow will have to be able to save user input. To make it easier install prompt-sync package and include it at the top of the file

const prompt = require('prompt-sync')({sigint: true});

The “{sigint: true}” part is required so the use could exit your app by pressing Ctrl (Command) + C.

Now we can start building our project!

const fetch = require('node-fetch');
const prompt = require('prompt-sync')({sigint: true});
class BookSearch { constructor(myBooks) {
this.myBooks = myBooks
}
...

We need that constructor so upon making an instance of the class we can initialize our object with myBooks property set to an empty array.

Here is what my base URL ended up looking like:

baseUrl = "https://www.googleapis.com/books/v1/volumes?q="

Let’s start with making a fetch request to the API

fetchBooks(query){
fetch(this.baseUrl+`${query}&projection=lite&key=${this.apiKey}`)
.then(res => res.json())
.then(data => {
this.filterData(data)
})
}

We are passing our returned data to filterData function. This is what the function looks like

filterData(data){  const filteredData = data.items.filter(volume => volume.volumeInfo.publisher).slice(0,5)  filteredData.forEach((volume, index) => this.displayVolumeData(index, volume.volumeInfo))  this.addBook(filteredData)}

We need to filter the data to make sure every returned book object has publisher property. Once that is done we display the data

displayVolumeData(ind, volume) {
console.log((ind + 1) + ")")
console.log("Title: " + volume.title)
console.log("Author: " + volume.authors[0])
console.log("Publisher: " + volume.publisher)
console.log("")
}

After the data is displayed we are calling addBook function inside our filterData function and passing filteredData as an argument because the book we are going to be adding to our reading list has to be one of the filteredData objects

filterData(data){  const filteredData = data.items.filter(volume => volume.volumeInfo.publisher).slice(0,5)  filteredData.forEach((volume, index) => this.displayVolumeData(index, volume.volumeInfo))  this.addBook(filteredData)}

Here is my addBook method. The main action happens here

addBook(data){  console.log("To add a book to your reading list enter the number of the book")
console.log("To see your reading list type 'list'")
console.log("To start a new search type 'search'")
let input = prompt("Enter search term: ") input = input.toLowerCase() if(Number(input) >= 1 && Number(input) <= data.length){ this.myBooks.push(data[input - 1])
this.addBook(data)
} else if (input === "list"){ console.log("")
console.log("YOUR READING LIST:")
this.myBooks === [] ? console.log("[]") : this.myBooks.forEach((book, ind) => this.displayVolumeData(ind, book.volumeInfo)) this.addBook(data) } else if (input === "search") { this.getBooks() } else if (input === "exit") { return } else { console.log("Please enter correct input") this.addBook(data) }}

I’m using the basic control flow in the function above. Notice that I’m using recursion technique in my method to provide the best user experience.

And here is my final method that has to be invoked to start the application

getBooks(){  console.log("Welcome to the Book Finder app!")
console.log("To exit the program any time type 'exit'")
const term = prompt('Enter search term: ') if(term === "exit") {
return
} else if (term.trim() === "" || Number(term)) {
console.log("Please enter correct input")
this.getBooks()
} else {
this.fetchBooks(term)
}
}

To start the app you have to create an instance of the BookSearch class and call getBooks method on it. To run the app just run node index.js in the terminal

const newSearch = new BookSearch([])newSearch.getBooks()

Happy coding!

--

--