UNPKG

astro-loader-goodreads

Version:
573 lines (458 loc) 17.8 kB
# `astro-loader-goodreads` [![npm version][npm-version-src]][npm-version-href] [![npm downloads][npm-downloads-src]][npm-downloads-href] [![License][license-src]][license-href] [![JSDocs][jsdocs-src]][jsdocs-href] [![bundle][bundle-src]][bundle-href] [![Astro][astro-src]][astro-href] [![Goodreads][goodreads-src]][goodreads-href] [![How to use Goodreads data in Astro ][how-to-use-src]][how-to-use-href] **Load Goodreads data for books in shelves/lists, user updates, and author blogs into Astro.** > [!NOTE] > `astro-loader-goodreads` uses the [Astro Content Loader API](https://docs.astro.build/en/reference/content-loader-reference/) to fetch data from Goodreads RSS feeds. > > See the [Usage](#usage) section below for instructions on how to use the package to load the fetched Goodreads data into [Astro Content Collections](https://docs.astro.build/en/guides/content-collections). --- ## Table of Contents - [Features](#features) - [Community Examples](#community-examples) - [Installation](#installation) - [Usage](#usage) - [Loader Options](#loader-options) - [Defining & Using Astro Content Collections](#defining--using-astro-content-collections) - [1. Goodreads Shelves](#1-goodreads-shelves) - [2. Goodreads User Updates](#2-goodreads-user-updates) - [3. Goodreads Author Blogs](#3-goodreads-author-blogs) - [Data Schema](#data-schema) - [Overview](#overview) - [1. `BookSchema`](#1-bookschema) - [2. `UserUpdateSchema`](#2-userupdateschema) - [3. `AuthorBlogSchema`](#3-authorblogschema) --- ## Features - **Load Bookshelves**: Import your Goodreads shelves to showcase your reading list, books you've read, or want to read. - **User Updates**: Display your latest Goodreads activity including reading status updates, reviews, and likes. - **Author Blogs**: Fetch author blogs from Goodreads to display updates from your favorite authors. - **Astro Content Collections**: Seamlessly integrates with Astro's content collection system for type-safe data access. ## Community Examples Below are some examples of websites that use `astro-loader-goodreads`. If you wish to add your site to this list, open a pull request! | Site | Page | Description | Source | | ------------------------------ | ------------------------------------------ | ------------------------------------------------------- | --------------------------------------- | | [sadman.ca](https://sadman.ca) | [sadman.ca/about](https://sadman.ca/about) | Books I'm currently reading and have recently finished. | [](https://github.com/sadmanca/blogv2) | --- ## Installation ```sh npm add astro-loader-goodreads ``` ## Usage ### Loader Options | Property | Description | Required | Default | | --------------------- | ----------------------------------------- | ---------- | ------- | | `url` | The URL of your Goodreads shelf, user, or author. | | - | | `refreshIntervalDays` | Number of days to cache data before fetching again from Goodreads. | | `0` | When `refreshIntervalDays` is set (e.g., to `7` for weekly updates), the loader will only fetch new data from Goodreads when that many days have passed since the last fetch. feat: Add optional loader option `refreshIntervalDays`. If not specified, no caching is done between builds (Astro's default data caching between page loads still applies). ### Defining & Using Astro Content Collections `astro-loader-goodreads` supports loading Goodreads data from 3 types of urls: 1. **[Shelves](#1-goodreads-shelves)**: Load books from a Goodreads shelf. 2. **[User Updates](#2-goodreads-user-updates)**: Load a Goodreads user's updates feed. 3. **[Author Blogs](#3-goodreads-author-blogs)**: Load a Goodreads author's blog. In your `content.config.ts` or `src/content/config.ts` file, you can define your content collections using each type of URL with the `goodreadsLoader` function. > [!NOTE] > For the full list of fields available for Astro content collections created using `astro-loader-goodreads`, see the [Data Schema](#data-schema) section below. --- ### 1. Goodreads Shelves **To load data for books from a Goodreads shelf, use the shelf's URL** (e.g. https://www.goodreads.com/review/list/152185079-sadman-hossain?shelf=currently-reading). `astro-loader-goodreads` will convert it to the correct RSS feed URL automatically. > [!IMPORTANT] > **The RSS feed for a Goodreads shelf only includes the last 100 books added to that shelf.** This means that if there are more than 100 books in a shelf, `astro-loader-goodreads` will not be able to retrieve them all. > > You can, however, create multiple shelves (e.g. _read-2025_, _read-2026_, etc.) and then create a content collection for each shelf to get around this limitation. ```typescript // src/content/config.ts import { defineCollection } from "astro:content"; import { goodreadsLoader } from "astro-loader-goodreads"; const currentlyReading = defineCollection({ loader: goodreadsLoader({ url: "https://www.goodreads.com/review/list/152185079-sadman-hossain?shelf=currently-reading", refreshIntervalDays: 7, // optional parameter; set to only fetch new data once per week }), }); export const collections = { currentlyReading }; ``` ```astro --- // src/pages/reading.astro import { getCollection } from "astro:content"; const books = await getCollection("currentlyReading"); --- <h1>Books I'm Currently Reading</h1> <div class="book-grid"> {books.map((book) => ( <div class="book-card"> <img src={book.data.book_large_image_url} alt={`Cover of ${book.data.title}`} /> <h2>{book.data.title}</h2> <p class="author">by {book.data.author_name}</p> {book.data.user_rating > 0 && ( <p class="rating">My rating: {book.data.user_rating}/5</p> )} <a href={book.data.link} target="_blank" rel="noopener noreferrer"> View on Goodreads </a> </div> ))} </div> <style> .book-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 2rem; } .book-card { border: 1px solid #eee; border-radius: 0.5rem; padding: 1rem; text-align: center; } .book-card img { max-width: 100%; height: auto; } </style> ``` --- ### 2. Goodreads User Updates **To load a Goodreads user's updates feed, use the user's profile URL** (e.g. https://www.goodreads.com/user/show/152185079-sadman-hossain). `astro-loader-goodreads` will convert it to the correct RSS feed URL automatically. > [!IMPORTANT] > **The RSS feed for a Goodreads user's updates only includes the last 10 updates by that user.** This means that `astro-loader-goodreads` cannot retrieve more than 10 updates for any single user. ```typescript // src/content/config.ts import { defineCollection } from "astro:content"; import { goodreadsLoader } from "astro-loader-goodreads"; const userUpdates = defineCollection({ loader: goodreadsLoader({ url: "https://www.goodreads.com/user/show/152185079-sadman-hossain", }), }); export const collections = { userUpdates }; ``` ```astro --- // src/pages/activity.astro import { getCollection } from "astro:content"; const updates = await getCollection("userUpdates"); // Sort updates by publication date (newest first) const sortedUpdates = updates.sort((a, b) => new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime() ); --- <h1>My Goodreads Activity</h1> <div class="activity-feed"> {sortedUpdates.map((update) => { const itemData = update.data.itemData; return ( <div class="activity-item"> <p class="date">{new Date(update.data.pubDate).toLocaleDateString()}</p> {itemData?.type === "ReadStatus" && ( <div class="read-status"> <img src={itemData.bookImgUrl} alt={`Cover of ${itemData.bookTitle}`} width="50" /> <div> <p> <strong>{itemData.readingStatus}</strong> <a href={itemData.bookUrl}>{itemData.bookTitle}</a> by {itemData.bookAuthor} </p> </div> </div> )} {itemData?.type === "Review" && ( <div class="review"> <img src={itemData.bookImgUrl} alt={`Cover of ${itemData.bookTitle}`} width="50" /> <div> <p> Rated <strong>{itemData.rating} stars</strong> for <a href={itemData.bookUrl}>{itemData.bookTitle}</a> by {itemData.bookAuthor} </p> </div> </div> )} {itemData?.type === "CommentReview" && ( <div class="comment-review"> <div> <p> Commented on <a href={itemData.reviewUrl}>{itemData.reviewUser}'s review</a> of <a href={itemData.bookUrl}>{itemData.bookTitle}</a>: </p> <blockquote>"{itemData.comment}"</blockquote> </div> </div> )} {/* Add additional item types as needed */} </div> ); })} </div> ``` --- ### 3. Goodreads Author Blogs **To load Goodreads author blog posts, use the author's URL** (e.g. https://www.goodreads.com/author/show/3389.Stephen_King). `astro-loader-goodreads` will append the necessary parameters to fetch the blog RSS feed automatically. ```typescript // src/content/config.ts import { defineCollection } from "astro:content"; import { goodreadsLoader } from "astro-loader-goodreads"; const authorBlog = defineCollection({ loader: goodreadsLoader({ url: "https://www.goodreads.com/author/show/3389.Stephen_King", }), }); export const collections = { authorBlog }; ``` ```astro --- // src/pages/author-updates.astro import { getCollection } from "astro:content"; const posts = await getCollection("authorBlog"); --- <h1>Latest Updates from Stephen Kingn</h1> <div class="blog-posts"> {posts.map((post) => ( <article class="blog-post"> <h2>{post.data.title}</h2> <p class="date">Published: {new Date(post.data.pubDate).toLocaleDateString()}</p> {post.data.content && ( <div class="content" set:html={post.data.content} /> )} <a href={post.data.link}>Read on Goodreads</a> </article> ))} </div> ``` --- ## Data Schema ### Overview The astro-loader-goodreads package provides three main schemas: 1. [`BookSchema`](#1-bookschema) - For books from Goodreads shelves 2. [`UserUpdateSchema`](#2-userupdateschema) - For user updates (with various activity types) 3. [`AuthorBlogSchema`](#3-authorblogschema) - For author blog posts ### 1. `BookSchema` This schema is used when loading data from a Goodreads shelf. ```typescript export const BookSchema = z.object({ id: z.coerce.string(), title: z.coerce.string(), guid: z.string(), pubDate: z.string(), link: z.string(), book_id: z.coerce.string(), book_image_url: z.string(), book_small_image_url: z.string(), book_medium_image_url: z.string(), book_large_image_url: z.string(), book_description: z.string(), num_pages: z.string().optional(), author_name: z.string(), isbn: z.coerce.string(), user_name: z.string(), user_rating: z.number(), user_read_at: z.string(), user_date_added: z.string(), user_date_created: z.string(), user_shelves: z.string().optional(), user_review: z.string().optional(), average_rating: z.number(), book_published: z.coerce.string(), }); ``` #### Book Fields | Field | Description | |-------|-------------| | `id` | Unique identifier for the book | | `title` | Book title | | `guid` | Global unique identifier for this entry | | `pubDate` | Publication date of this entry in the feed | | `link` | URL to the book's Goodreads page | | `book_id` | Goodreads ID for the book | | `book_image_url` | URL to the book cover image | | `book_small_image_url` | URL to small version of book cover (50×75 px) | | `book_medium_image_url` | URL to medium version of book cover (65×98 px) | | `book_large_image_url` | URL to large version of book cover (316×475 px) | | `book_description` | Description/synopsis of the book | | `num_pages` | Number of pages in the book (optional) | | `author_name` | Name of the book's author | | `isbn` | International Standard Book Number | | `user_name` | Username of who added the book to their shelf | | `user_rating` | Rating given by the user (0-5) | | `user_read_at` | Date when the user finished reading the book | | `user_date_added` | Date when the book was added to the user's shelf | | `user_date_created` | Date when this entry was created | | `user_shelves` | List of shelves the user assigned to this book (optional) | | `user_review` | User's review of the book (optional) | | `average_rating` | Average rating on Goodreads | | `book_published` | Book's original publication date | ### 2. `UserUpdateSchema` This schema is used when loading data from a Goodreads user's updates feed. ```typescript export const UserUpdateSchema = z.object({ id: z.string(), title: z.string(), link: z.string().optional(), description: z.string().optional(), pubDate: z.string(), itemType: z.string().optional(), itemData: ItemDataSchema.optional() }); ``` ### `UserUpdateSchema` Item Types The `itemData` field contains a discriminated union based on the `type` field: #### `AuthorFollowing` When a user follows an author: ```typescript { type: "AuthorFollowing", followId: string, userUrl: string, authorId: string } ``` #### `UserStatus` When a user reports progress on a book: ```typescript { type: "UserStatus", userUrl: string, percentRead: string, bookUrl: string, bookTitle: string, bookAuthor: string, bookImgUrl: string } ``` #### `ReadStatus` When a user changes their reading status: ```typescript { type: "ReadStatus", userUrl: string, readingStatus: string, // 'started reading', 'wants to read', or 'finished reading' bookUrl: string, bookTitle: string, bookAuthor: string, bookImgUrl: string } ``` #### `Review` When a user posts a review: ```typescript { type: "Review", userUrl: string, rating: number, bookUrl: string, bookTitle: string, bookAuthor: string, bookImgUrl: string } ``` #### `LikeReview` When a user likes someone's review: ```typescript { type: "LikeReview", userUrl: string, reviewUrl: string, reviewUser: string, bookUrl: string, bookTitle: string, bookImgUrl: string } ``` #### `LikeReadStatus` When a user likes someone's read status: ```typescript { type: "LikeReadStatus", userUrl: string, readStatusUser: string, readStatusUserImgUrl: string, readStatus: string, bookUrl: string, bookTitle: string } ``` #### `CommentStatus` When a user comments on a status: ```typescript { type: "CommentStatus", userUrl: string, statusUrl: string, statusUser: string, comment: string } ``` #### `CommentReview` When a user comments on a review: ```typescript { type: "CommentReview", userUrl: string, reviewUrl: string, reviewUser: string, bookUrl: string, bookTitle: string, bookAuthor: string, comment: string } ``` ### 3. `AuthorBlogSchema` This schema is used when loading data from a Goodreads author's blog. ```typescript export const AuthorBlogSchema = z.object({ id: z.string(), title: z.string(), link: z.string(), description: z.string(), pubDate: z.string(), author: z.string().optional(), content: z.string().optional(), }); ``` ### `AuthorBlogSchema` Fields | Field | Description | |-------|-------------| | `id` | Unique identifier for the blog post | | `title` | Blog post title | | `link` | URL to the blog post | | `description` | Raw HTML description of the blog post | | `pubDate` | Publication date | | `author` | Author's name (if available) | | `content` | Main content of the blog post (if available) | --- ## License `astro-loader-goodreads` is [MIT licensed](https://github.com/sadmanca/astro-loader-goodreads/blob/main/LICENSE). --- Built with by [@sadmanca](https://github.com/sadmanca)! [npm-version-src]: https://img.shields.io/npm/v/astro-loader-goodreads?style=flat&logo=npm&colorA=ea2039&colorB=2e2e2e [npm-version-href]: https://npmjs.com/package/astro-loader-goodreads [npm-downloads-src]: https://img.shields.io/npm/dm/astro-loader-goodreads?style=flat&logo=npm&colorA=ea2039&colorB=2e2e2e [npm-downloads-href]: https://npmjs.com/package/astro-loader-goodreads [license-src]: https://img.shields.io/badge/license-MIT-080f12?style=flat&colorA=2e2e2e&colorB=blue [license-href]: https://github.com/sadmanca/astro-loader-goodreads/blob/main/LICENSE [jsdocs-src]: https://img.shields.io/badge/jsdocs-astro--loader--goodreads-080f12?style=flat&colorA=2e2e2e&colorB=525252 [jsdocs-href]: https://www.jsdocs.io/package/astro-loader-goodreads [bundle-src]: https://img.shields.io/bundlephobia/minzip/astro-loader-goodreads?style=flat&colorA=2e2e2e&colorB=1f8f00 [bundle-href]: https://bundlephobia.com/result?p=astro-loader-goodreads [astro-src]: https://img.shields.io/badge/Astro-0690FA?style=flat&logo=astro&logoColor=ffffff&color=5e15a1 [astro-href]: https://astro.build [goodreads-src]: https://img.shields.io/badge/Goodreads-0690FA?style=flat&logo=goodreads&logoColor=7e470f&color=e9e4d0 [goodreads-href]: https://goodreads.com [how-to-use-src]: https://img.shields.io/badge/How_to_use_Goodreads_data_in_Astro-0690FA?style=flat&logo=amp&logoColor=6854ff&color=2e2e2e [how-to-use-href]: https://sadman.ca/posts/how-to-use-goodreads-data-in-astro/