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 in Action: Real-World Examples and ProjectsJavaScript in Action: Real-World Examples and ProjectsApr 19, 2025 am 12:13 AM

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.

JavaScript and the Web: Core Functionality and Use CasesJavaScript and the Web: Core Functionality and Use CasesApr 18, 2025 am 12:19 AM

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

Understanding the JavaScript Engine: Implementation DetailsUnderstanding the JavaScript Engine: Implementation DetailsApr 17, 2025 am 12:05 AM

Understanding how JavaScript engine works internally is important to developers because it helps write more efficient code and understand performance bottlenecks and optimization strategies. 1) The engine's workflow includes three stages: parsing, compiling and execution; 2) During the execution process, the engine will perform dynamic optimization, such as inline cache and hidden classes; 3) Best practices include avoiding global variables, optimizing loops, using const and lets, and avoiding excessive use of closures.

Python vs. JavaScript: The Learning Curve and Ease of UsePython vs. JavaScript: The Learning Curve and Ease of UseApr 16, 2025 am 12:12 AM

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

Python vs. JavaScript: Community, Libraries, and ResourcesPython vs. JavaScript: Community, Libraries, and ResourcesApr 15, 2025 am 12:16 AM

Python and JavaScript have their own advantages and disadvantages in terms of community, libraries and resources. 1) The Python community is friendly and suitable for beginners, but the front-end development resources are not as rich as JavaScript. 2) Python is powerful in data science and machine learning libraries, while JavaScript is better in front-end development libraries and frameworks. 3) Both have rich learning resources, but Python is suitable for starting with official documents, while JavaScript is better with MDNWebDocs. The choice should be based on project needs and personal interests.

From C/C   to JavaScript: How It All WorksFrom C/C to JavaScript: How It All WorksApr 14, 2025 am 12:05 AM

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

JavaScript Engines: Comparing ImplementationsJavaScript Engines: Comparing ImplementationsApr 13, 2025 am 12:05 AM

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

Beyond the Browser: JavaScript in the Real WorldBeyond the Browser: JavaScript in the Real WorldApr 12, 2025 am 12:06 AM

JavaScript's applications in the real world include server-side programming, mobile application development and Internet of Things control: 1. Server-side programming is realized through Node.js, suitable for high concurrent request processing. 2. Mobile application development is carried out through ReactNative and supports cross-platform deployment. 3. Used for IoT device control through Johnny-Five library, suitable for hardware interaction.

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 Tools

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

DVWA

DVWA

Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.