---
title: "Managing my reading list"
date: 2022-06-12T00:00:00
slug: managing-my-reading-list

---
A little while ago I was toying with building a lightweight web app that would make it easy to manage and share reading lists. I never got around to making it, but instead I built a very-MVP version by adding my [reading list](/reading-list) to this website. It was a fairly simple construction, I simply added a file called `reading-list.11tydata.json` to my source directory, that looked like this:

```json
{
    "books": [
        {
            "title": "Eyes of the Void",
            "author": "Adrian Tchaikovsky",
            "goodreads": "https://www.goodreads.com/book/show/58950674-eyes-of-the-void",
            "status": "completed",
            "completed_date": "25/05/22"
        },
        {
            "title": "The Player of Games",
            "author": "Iain M. Banks",
            "goodreads": "https://www.goodreads.com/book/show/18630.The_Player_of_Games",
            "status": "started"
        },
    ]
}
```

Then, on my reading list page I could simply iterate over the `books` variable and output the data however I pleased. And it worked absolutely _fine_.

But it was't quite what I wanted, it doesn't give any information about the books I'm reading, e.g. if I wanted to add book covers I'd have to upload them manually. So, last night, when I really should have been sleeping, I started working on using data files, combined with [using pagination to construct pages from data](https://www.11ty.dev/docs/pages-from-data/), to build something that was slightly closer to my original ambition.

## Getting information about a book

Choosing a data source was pretty easy. [Open Library](https://openlibrary.org) has a very simple route-based API that would allow me to do all the things I needed to: search for a book by author and title, get information about that book, and get the book covers.

I did all this in a data file inside the `_data` directory, called `readinglist.js`. I added my original list of books, and then for each book I use the API to first search for the book, and choose the first result, then I get more detailed information about the book using the `Works` API, and then finally I look for a cover if it exists. Here's all the code to do that:

```javascript
const getBook = async book => {
        const res = await axios.get(`https://openlibrary.org/search.json?author=${book.author}&title=${book.title}`);
        const key = res.data.docs[0].key;
        const work = (await axios.get(`https://openlibrary.org${key}`)).data;
        const cover =  work.covers ? `https://covers.openlibrary.org/b/id/${work.covers[0]}-M.jpg` : undefined;

        return {...book, cover, "data": work };
};

module.exports = async function() {
    return await Promise.all(books.map(getBook));
}
```

## Constructing pages from the data

**Update:** I removed this feature as it wasn't particularly valuable and was slowing the build down

Eleventy makes this incredibly easy. I just had to add some pagination rules to the front-matter data on a new template:

```yaml
layout: layout.njk
pagination:
    data: readinglist
    size: 1
    alias: book
permalink: book/{{ book.title  | slugify }}/index.html
eleventyComputed:
    title: "{{ book.title }} - {{ book.author }}"
---
```

Eleventy took care of the rest, and generated all of the pages. I did find that I had to specify the `.html` extension for the permalinks. If I left it out, Eleventy wouldn't generate a valid HTML page and instead navigating to it would download the file instead of displaying it in-browser.

## The result

You can see the results for yourself on the [reading list](/reading-list/), ~~or by viewing one of the information pages for a book, such as [Murder on the Orient Express](/book/murder-on-the-orient-express)~~. Overall, I'm actually really happy with how it turned out. It's a step closer to the web app that I'd originally envisioned, but it only took a few hours to put together.

There are a few limitations: I can't fine-tune what version of the book the API returns without more work, and I've got no control over the descriptions provided. But, I think it's a fair compromise to achieve what I wanted.

Overall, this was a fun little late-night project to pick up. As usual, I love how easy Eleventy makes tasks like this.

