search
HomeWeb Front-endJS TutorialFun with Hardcover&#s Book API: A quick reference

I'm a longtime Goodreads user, but have always wanted to try something better when it comes to tracking my books. So I've recently started using Hardcover, a competitor run by a small indie team. It's still in the early days, and I know it's hard to beat something as large as Goodreads, but I'm hopeful!

One of the great things about Hardcover is that, unlike Goodreads, it has an API that is free-to-use and lets you query for any data on the books that are stored in Hardcover. It also can be used to do basically anything you would be able to do yourself in the Hardcover UI, like update the status of books you are
reading, add books to lists and more.

How I'm using Hardcover's API

Right now I'm using the API to generate the book review section of my site. I fetched all my reviews from Hardcover, and am storing them in a JSON file. I then loop through the reviews in this list, and render them all on the page.

I wasn't too sure what to do about really short reviews, so for the moment I've settled on rendering the reviews as-is on the page if they are under 360 characters, and linking out to a separate page if they are more than that.

Fun with Hardcover

I also added a search bar, which will search through all my reviews, and I have some text blurring set up if there are spoilers on the review.

Fun with Hardcover

In the future I'd like to add a way to filter by the star rating, and by genre as well.

Getting started with Hardcover's API

Hardcover's API is also still in an early-access mode, and there isn't any proper documentation yet, so in this post I'll go over some of the queries I found useful so far. Hardcover's Discord server is also a great place to get answers to your questions.

You'll first need to go to your settings page, and get your Hardcover API key. Then you can test out your queries in Hardcover's GraphQL console.

Getting all the books you have read

Starting off with a basic GraphQL query, we can filter using the status_id to get a list of titles of all the books you have marked as “read” in Hardcover:

{
  me {
    user_books(where: {status_id: {_eq: 3}}) {
      rating
      book {
        title
      }
    }
  }
}

We've wrapped it in me which is what you can use to query on anything specific to your user.

The way the status_id value works is:

  • 1: A “want to read” book
  • 2: An “currently reading” book
  • 3: A “read” book
  • 5: “A did not finish” book

Getting the author of a book

If you search for cached_contributors, you'll get an array containing a list of “contributors” to a book. This will contain a predetermined set of data, like the contributor's name, ID, and image.

{
  me {
    user_books(where: {status_id: {_eq: 3}}) {
      rating
      book {
        title
      }
    }
  }
}

The reason they're “contributors” and not “authors” is that it can also contain the names of people who have translated the book. If there are multiple authors, they will also all be in the list. If you're querying on a regular fiction book with one author, using the first item in the list will usually be fine.

The cached version is faster to query, but if there was something specific you wanted, you can also query on the non-cached contributions:

book {
  cached_contributors
}

Getting a book's Hardcover URL

If you wanted to get the book's link on Hardcover, you can query for its slug:

book {
  title
    contributions {
      author {
        name
       }
    }
 }

A slug is the string after the domain name of the website, e.g. on emgoto.com/hardcover-book-api, the “hardcover-book-api” bit is the slug.

So once you get the slug, you'll just need to prepend https://hardcover.app/books/ to the beginning of it to create your Hardcover URL.

Getting the genre tags on a book

The genre tag system in Hardcover is user-generated. You can query on the cached_tags, which will return you the tags in order from most-tagged to least.

book {
  slug
}

Once you have the full list of tags, you can use cached_tags['Genre'] to get the genre-specific ones.

If a lot of people have tagged a particular book as fiction, that will be the first genre that shows up in the list. Interestingly people love to tag their books as fantasy, and so this genre often shows up before the fiction tag. People love tagging their books as fantasy so much, that sci-fi books like Dune even end up with both sci-fi and fantasy tags as well.

If you are going to use this data, I recommend doing a little bit of clean-up on it first. For example if the book has both fantasy and sci-fi as a genre tag, only use the one that comes first in the list and discard the other one, since that is more likely to be accurate.

Adding a book to your “to read" list

So far I've only touched on fetching data, but you can also use Hardcover's API to manipulate data - of course you can't touch anyone else's things, but anything that you could do on your own Hardcover account is fair game.

If you have a book's ID, you can add it to your “to read” list by setting its status_id to 1:

book {
  cached_tags
}

Getting your book reviews

This is what I use to fetch all the reviews I have written in Hardcover:

{
  me {
    user_books(where: {status_id: {_eq: 3}}) {
      rating
      book {
        title
      }
    }
  }
}

Nearly all of my books and reviews are imported from Goodreads, and I think sometimes the data gets a little bit messed up in the import. I found that sorting it by both dated_added and reviewed_at was more accurate.

I used the review_raw value to get the review text, which is doesn't include any formatting like newlines. This unfortunately means if you have multiple paragraphs in your review, the API spits it out all as one long paragraph like this:

book {
  cached_contributors
}

With JavaScript, I got around this by doing a regex like the following:

book {
  title
    contributions {
      author {
        name
       }
    }
 }

If there are any periods with no spaces after them, you can make a guess that this should be a new paragraph and add the double newline nn. Which creates a new paragraph.

Another downside is that spoiler tags are also missing, so you'll have to add these in manually.

There is also a review_html value, which I thought might be more useful, but unfortunately it always seems to be null for me. Similarly, if you have a review with spoilers, there is a review_has_spoilers value, but for all the books I've imported from Goodreads, this value is false so you may not be able to rely on it.

Searching for a book by title and/or author

Hardcover's book search is pretty accurate, but they don't have a 1-1 version of this in their API. There's a lot of ways you could try and mimic their search behaviour, but a quick way to get started would be to search on title, using _eq:

book {
  slug
}

I ordered the list of books by users_read_count, as the likelihood is that the book called Dune with the highest number of readers is going to be the Dune that you are looking for.

If you want to filter by title and author, the filter query will look like this:

book {
  cached_tags
}

If you didn't want to do exact string matching, you can use the _ilike query instead, which doesn't match on cases, so using lower case will still work:

mutation addBook {
  insert_user_book(object: {book_id: 123, status_id: 1}) {
    id
  }
}

You can also use % characters as wildcards with _ilike, so you can do

{
  me {
    user_books(
      where: { _and: [ 
        {has_review: {_eq: true}},
        {status_id: {_eq: 3 }}
      ]}
      order_by: [
        { date_added: desc },
        { reviewed_at: desc }
      ]
    ) {
      reviewed_at
      date_added
      review_raw
      rating
      book {
        title
      }
    }
  }
}

Which will match any author that contains the word “frank”.

As a side note, since Hardcover uses Hasura, Googling for “how to do X in Hasura” will generally bring up how you can do these sort of more complicated queries.

Getting books in your Hardcover Lists

As well as the standard “want to read” and “read” lists, Hardcover also has a separate custom lists feature. To grab all of your lists, and the books inside of them, you can do the following:

this is the end of one paragraph.And this is the start of the next

Adding a book to a list

If you wanted to add a book to a list, first you would need to grab the list ID and the book's ID. Then it's easy as the following:

{
  me {
    user_books(where: {status_id: {_eq: 3}}) {
      rating
      book {
        title
      }
    }
  }
}

Making an API call with JavaScript

Once you want to move out of the GraphQL console, you can make API calls using fetch(). As a really quick example, here's me fetching all my reviews (I've abbrievated it down a bit):

book {
  cached_contributors
}

A programming pro-tip that I have to make constructing fetch calls easier is if you:

  • Go to Hardcover
  • Search for graphql in the Network tab
  • Find one that looks useful and is returning info
  • Right click the network call > Copy > Copy as fetch

It will copy for you a fetch call similar to the one I've pasted above, which you can then use in your own code.

Fun with Hardcover

A note on rate limiting

Once you move out of the GraphQL console and start doing things in a script, you may run into errors or rate limiting issues if you try and do too many things at the same time.

For example, when adding a new book to a list, I find that trying to add two at the same time will error in the API, probably because it is trying to add two books to the same position in the list.

Similarly, if you tried to make 100 different calls to search for a book based on its title, some of these calls will time out. If you spread them out and do one every second like the following, then you shouldn't have any issues:

book {
  title
    contributions {
      author {
        name
       }
    }
 }

Also, if you grabbed the image URL of a book from Hardcover, and then attempted to load the images of 100 books at the same time on your page, the API will rate limit you, and some of the images will not load. I recommend putting a loading=lazy in your image tags like so:

book {
  slug
}

This way, the images will only load when a user scrolls down to view them.

And that's it! I'm keen to see where Hardcover goes next - I'm hoping it does well, and finally we have a Goodreads-killer on our hands. If you'd like to follow me on Hardcover, I fortuitously have managed to snag the @emma handle.

The above is the detailed content of Fun with Hardcover&#s Book API: A quick reference. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Javascript Data Types : Is there any difference between Browser and NodeJs?Javascript Data Types : Is there any difference between Browser and NodeJs?May 14, 2025 am 12:15 AM

JavaScript core data types are consistent in browsers and Node.js, but are handled differently from the extra types. 1) The global object is window in the browser and global in Node.js. 2) Node.js' unique Buffer object, used to process binary data. 3) There are also differences in performance and time processing, and the code needs to be adjusted according to the environment.

JavaScript Comments: A Guide to Using // and /* */JavaScript Comments: A Guide to Using // and /* */May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript: A Comparative Analysis for DevelopersPython vs. JavaScript: A Comparative Analysis for DevelopersMay 09, 2025 am 12:22 AM

The main difference between Python and JavaScript is the type system and application scenarios. 1. Python uses dynamic types, suitable for scientific computing and data analysis. 2. JavaScript adopts weak types and is widely used in front-end and full-stack development. The two have their own advantages in asynchronous programming and performance optimization, and should be decided according to project requirements when choosing.

Python vs. JavaScript: Choosing the Right Tool for the JobPython vs. JavaScript: Choosing the Right Tool for the JobMay 08, 2025 am 12:10 AM

Whether to choose Python or JavaScript depends on the project type: 1) Choose Python for data science and automation tasks; 2) Choose JavaScript for front-end and full-stack development. Python is favored for its powerful library in data processing and automation, while JavaScript is indispensable for its advantages in web interaction and full-stack development.

Python and JavaScript: Understanding the Strengths of EachPython and JavaScript: Understanding the Strengths of EachMay 06, 2025 am 12:15 AM

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScript's Core: Is It Built on C or C  ?JavaScript's Core: Is It Built on C or C ?May 05, 2025 am 12:07 AM

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript Applications: From Front-End to Back-EndJavaScript Applications: From Front-End to Back-EndMay 04, 2025 am 12:12 AM

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

Python vs. JavaScript: Which Language Should You Learn?Python vs. JavaScript: Which Language Should You Learn?May 03, 2025 am 12:10 AM

Choosing Python or JavaScript should be based on career development, learning curve and ecosystem: 1) Career development: Python is suitable for data science and back-end development, while JavaScript is suitable for front-end and full-stack development. 2) Learning curve: Python syntax is concise and suitable for beginners; JavaScript syntax is flexible. 3) Ecosystem: Python has rich scientific computing libraries, and JavaScript has a powerful front-end framework.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Integrate Eclipse with SAP NetWeaver application server.

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Powerful PHP integrated development environment

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),