How I used the Goodreads API to pick my next read
Tags: javascript, beginners, showdev
Published: 2019-10-14 15:32:24

##Introduction

I like to read. A lot. I also (shockingly enough) like to code. So a little while ago when I had the idea to combine these two interests of mine, I hit up the [Goodreads API docs](https://www.goodreads.com/api) to see what sorts of data I could get my hands on. After perusing the docs (aka doing a lot of command + f to find things because the docs aren't the most user-friendly), I decided to make a small program that would pick out a book for me to read next. 

##Getting Started

Since I had already picked out the language I was going to use, I dove right in and created the project directory on my Desktop and initialized it with Git. 

If you're using this as a tutorial and haven't worked with JavaScript previously, you should download [Node.js](https://nodejs.org/) and get yourself a package manager. I currently use npm. 

I also knew from the docs that I needed to get myself an API key. As a Goodreads user, all I had to do was login to get them. I believe non-Goodreads users will need to create an account to get access.

##Getting Down to Business
For this all I needed was a list of the books I had on my to-read shelf. After the aforementioned "command + f"-ing, I found the GET request to ["get the books on a members shelf"](https://www.goodreads.com/api/index#reviews.list) which, for no reason other than previous experience with it, led me to want to use the [request promise](https://www.npmjs.com/package/request-promise) package.

At this point, I also decided to install [dotenv](https://www.npmjs.com/package/dotenv) so I could pass in my API key and other info that I didn't want hardcoded.

I set up my .env file in the following format:

```
KEY=YOUR_GOODREADS_KEY
SECRET=YOUR_GOODREADS_SECRET
USER_ID=YOUR_GOODREADS_ID
VERSION=2
PER_PAGE=200
SHELF=to-read
```

Then I created my main.js file. My first objective was to log out what Goodreads returned from my request. Much to my surprise, there was a nice long chunk of XML in my terminal.

![Gif of Lauren Conrad going "whoa!"](https://thepracticaldev.s3.amazonaws.com/i/5gn6z4acxi2lt9yc9iw6.gif)
<figcaption>Lauren Conrad saying, "Whoa."

<a href="https://giphy.com/gifs/transparent-reaction-AqEUAOCWjoU5W">via GIPHY</a>
</figcaption>

One of the good things about the documentation is that, if you're logged in and have an API key, when you click on the sample URL for the request you want to make it'll show you what the API returns. 

```
var request = require('request-promise');

require('dotenv').config();

let options = {
    method: 'GET',
    uri: `https://www.goodreads.com/review/list/${process.env.USER_ID}.xml`,
    qs: {
        key: process.env.KEY,
        v: process.env.VERSION,
        shelf: process.env.SHELF,
        per_page: process.env.PER_PAGE
    }
}

request(options).then((shelf) => {
    console.log(shelf);
}).catch(err => console.error(err));

```

Even though I had already seen the sample URL and what it returned, I was still surprised to see the XML in terminal so I did a quick perusal of the Goodreads developer forums to see if there was a JSON endpoint for this. 

Spoiler alert: there's not.

![DJ from Full House sighing dramatically](https://thepracticaldev.s3.amazonaws.com/i/cxi0aliwrqved4g659zq.gif)
<figcaption>DJ from Full House sighing dramatically. 

<a href="https://giphy.com/gifs/90s-hmpqVzwPARTmo">via GIPHY</a></figcaption>

After doing some quick searching, I decided to install [xml2js](https://www.npmjs.com/package/xml2js) so I could get the response into a more manageable and readable format.

I like to work incrementally so after requiring the new package with `var xml2js = require('xml2js');`, I modified my `.then()` block to parse the response and logged out the end result of that.

```
request(options).then((shelf) => {
    xml2js.parseString(shelf, function (err, result) {
        console.log(result);
    });
}).catch(err => console.error(err));
```

Now that I had some nice JavaScript object action going on, it was just a matter of figuring out how to access the titles within the arrays and objects. 

My first step was getting access to the list of books:

`let books = result['GoodreadsResponse']['reviews'][0]['review'];`

The books were stored in an array which meant randomly selecting an index value was just a matter of picking a number from 0 to the last index in the array.

`let index = Math.floor(Math.random() * books.length);`

Normally, I like to set up intermediate variables when I'm traversing through dense objects and arrays like this but since the only thing I needed was the title and I wasn't going to be doing any more operations, I figured I'd skip the intermediate variables and put it all in the assignment for the title.

`let title = books[index]['book'][0]['title'][0];`

At this point, the only thing left to do was print out the title and run the app!

```
var request = require('request-promise');
var xml2js = require('xml2js');

require('dotenv').config();

let options = {
    method: 'GET',
    uri: `https://www.goodreads.com/review/list/${process.env.USER_ID}.xml`,
    qs: {
        key: process.env.KEY,
        v: process.env.VERSION,
        shelf: process.env.SHELF,
        per_page: process.env.PER_PAGE
    }
}

request(options).then((shelf) => {
    xml2js.parseString(shelf, function (err, result) {
        let books = result['GoodreadsResponse']['reviews'][0]['review'];
        let index = Math.floor(Math.random() * books.length);
        let title = books[index]['book'][0]['title'][0];
        console.log(title);
    });

}).catch(err => console.error(err));
```

## Next Steps
Although I don't fancy myself a designer or particularly skilled in terms of creating visually appealing things, I think my next steps will be to make a UI that shows more information like the cover, the author, and the rating and deploy it to Heroku. I may even make a fun feature where users can enter their Goodreads user id to have it randomly select a book from their to-read shelf.

## Final Thoughts
I've always found it difficult to do side projects outside of work because I could never come up with an idea I liked enough to dedicate time to when I could be reading or doing something else I like to do in my free time. But I think this was a fun way to combine two things I enjoy into a project I may actually use.