By Adam Fortuna
Back in September I started looking into ways of speeding up the Hardcover website, iOS and Android apps while also lowering our hosting costs. The main site was running on Next.js (app dir) and was hosting was starting to get expensive. It grew to $600/month on Vercel, then lowered when we moved to Google Cloud Run, then grew again over time as more readers joined.
Throughout that time, I tried a bunch of things to speed up the site, but to solve the real problems would require a LOT of code. The problem was that Hardcover was originally engineered to run entirely in the browser with no backend. When we switched to running on the server (Next.js 14 w/RSC), things sped up a little, but each request still needed to go through a bunch of layers (Next.js, Hasura, Rails, Postgres).
After a bunch of research, and a lot of soul searching, I decided it was time for a major change: we’d switch from Next.js to Ruby on Rails for the entire front-end. This wasn’t a decision I made lightly. I knew it would be a TON of work, but if successful the site would be faster, easier to maintain and cost less to host – wins all around.
So we started the migration – a plan to move to Inertia.js and Ruby on Rails. Since our backend was already Rails, and our front-end was React, the big shift in release was changing how data is loaded and sent to the browser when you load a page.
I always liked organization in release names, like Androids alphabetical dessert themed names. I thought we’d do something similar using libraries and literary themes. The Library of Alexandria seemed a perfect theme to start with. 😂
Previously, Next.js would hit a GraphQL server which would hit either Postgres or Rails, then return the contents and render out the page from there. Some parts of that were cached – at the Postgres, GraphQL and Next.js levels – but much more needed to be cached in Next.js using Redis.
Now Rails will check the cache (Solid Cache with Postgres) for the content and render the page immediately. If it’s not cached, it’ll use an existing database connection to load data, cache it and return it. This heavy emphasis on caching will be a HUGE speed improvement. Uncached pages will still be fast, but they won’t be instant fast.
Ok, that was all very technical. I’m going to try to list out everything that’s included in this release, but I’ll be a little brief.
On the profile page, you’ll now see a little teaser for “Shared Reads”.
Clicking into the page will show every book that both you and that reader have read. We’ve divided it up into four groups: Books you both rated 4.5+ ⭐, ones you rated 2 ⭐ higher, ones they rated 2 ⭐ higher, and all books.
You can try this out with anyone right now:
If you’re a content creator on BookTok, Bookstagram or BookTube, please reach out to me (adam at hardcover.app)! I’d love to feature you here in the future.
A few years ago, before Hardcover, I used to do write ups about my past year in books on my blog. One year I realized I’d read 100 books, but only 10 were by women! The following year I intentionally read more books by women authors and saw that percentage grow to about 40% of my books from the year.
I only realized that because I was tracking and had the data. For others who are also data driven, we’ve improved Hardcover Goals to now support and celebrate more diversity in your reading.
This allows you to create goals that use any (or all) of the following:
The author demographics are only as good as our database. If you spot an author not listed in a goal, or listed incorrectly, you can help out by updating that author or filing a report for us to update them.
Supporters can now upload a profile header, or use one of the ones we provide. Down the line we’ll also allow Supporters to set custom headers for Lists.
Librarians can now edit and mark both of these as duplicates. Publishers can also have a “parent” publisher – which can be used for imprints and alternative names.
Last year we released what we called Letterbooks, an improved way to showcase a list of books inspired by Letterboxd. These allow for changing the view (table, card or shelf), and sorting by a number of options.
Letterbooks are now available almost everywhere we show a list of books. That includes:
In each of these, you’ll be able to sort by Match Percentage if you’re a Supporter. We’re planning to add filtering and bulk edit mode to each of these areas soon too!
Speaking of Genres, Moods and Tags – they’re back! We disabled these pages because of how slow they were due to how we were generating them. Now they’re much faster.
We’ve also changed the sorting of them. Previously they were sorted by book popularity within that tag. Unfortunately, because anyone can tag a book, that meant that most popular books ended up being at the top of every genre. 😱
Now we’re sorting these by the number of readers who have tagged them with that genre, mood or tag. In other words, the top “Fantasy” book is the one that the most readers on Hardcover have tagged with a genre of “Fantasy” (Spoiler: Harry Potter #1 and Mistborn at the top of the list).
You can now edit dates for your Reading Journal! 🥳 This is only available for entries you create – not the ones created by the system.
We were very ambitious with our ideas for new stats. 😂 Ste and I designed these during over a month of Hardcover Live episodes you can check out to see how we landed where we did.
What we’re releasing today is the Preview of stats. Finishing this up is at the very top of the list right now, alongside any bug fixes and the release. You can give it a shot today to see what about 25% of it will look like.
If you signed up monthly using Stripe, we didn’t have an option to upgrade to yearly before. You can now upgrade from your Membership page.
Most of the other changes are less noticeable – increasing the width of pages, improving the way a group of books looks on mobile, changing how book reviews show up and making the site faster. 🚀
We also removed a few things as part of this release.
I’ll be posting a series of articles about this. This was a major code update that spanned thousands of files and even new services to support the move. Here’s the high level list of changes.
books.featured_book_series_id
and books.cached_featured_series
to show top series for a book (API users: use this to easily get the top series!)id
to the authors in books.cached_contributors
action_at
column to reading_journals (editable by users)taggable_counts
table which caches the number of times something has been tagged (speeds up for the genre/mood/tag listing pages!).You can use this to get the top genres for a book (or just use the books.cached_tags column
).
query MyQuery {
books(where: {slug: {_eq: "hyperion"}}) {
taggable_counts(order_by: {count: desc}, limit: 10, where: {tag: {tag_category: {slug: {_eq: "genre"}}}}) {
tag {
tag
slug
tag_category {
category
}
}
count
}
}
}
Code language: JavaScript (javascript)
users.cached_cover
for supporters to set a header image on their profileOver the next few weeks I’m going to write a few articles about this migration. Once I write these I’ll link them here:
If you have any technical questions or just want to chat about that side of Hardcover, feel free to join the #development channel on Discord.
Well, lots of sleep for one. 😂 This project took a while.