Skip to content

fix: overhaul Google Books API (429/403/no-results) + add Open Library provider#158

Open
oRadiatorExtrem wants to merge 1 commit into
anpigon:masterfrom
oRadiatorExtrem:fix/overhaul-api
Open

fix: overhaul Google Books API (429/403/no-results) + add Open Library provider#158
oRadiatorExtrem wants to merge 1 commit into
anpigon:masterfrom
oRadiatorExtrem:fix/overhaul-api

Conversation

@oRadiatorExtrem

@oRadiatorExtrem oRadiatorExtrem commented Jun 4, 2026

Copy link
Copy Markdown

Summary

Test plan

  • Search "Atomic Habits" without API key -> results or clear rate-limit notice
  • Invalid API key -> "Access denied (403)" notice
  • ISBN-13 search with Google Books -> correct book
  • Switch to Open Library -> works without any API key
  • ISBN search with Open Library
  • Settings "Test Key" -> shows success/failure reason
  • Locale default -> no langRestrict applied

Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Introduced Open Library as an additional book search provider option.
    • Added automatic ISBN detection for direct book searches.
    • Improved API error handling with specific error messages for rate limiting and access denials.
    • Enhanced settings interface for provider selection and management.

- Fix 429/403 errors: use requestUrl throw:false, intercept status codes
  with clear user-facing messages
- Fix langRestrict bug: normalize locale (en-US->en), skip restriction
  on default locale -- root cause of issue anpigon#153 (no results)
- Fix ISBN search: strip dashes, use isbn: prefix, skip langRestrict
- Add coverMediumUrl/coverLargeUrl from Google Books zoom parameter
- Add Open Library as new provider (no API key, free, global)
  with ISBN search and S/M/L cover images
- Improve API key test button: does real search instead of string check
- Add warning about Google Books rate limiting without API key
- Format all files with prettier (pre-existing lint debt)
- Bump version to 0.8.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 4, 2026 18:42
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds Open Library as a third book search provider to the plugin, extending the existing Google Books and Naver support. It includes HTTP error handling improvements, ISBN detection and locale refactoring in Google Books, the complete Open Library API implementation, UI configuration wiring, and build configuration updates for version 0.8.0.

Changes

Open Library Search Provider

Layer / File(s) Summary
Service provider enum and factory
src/constants.ts, src/apis/base_api.ts
ServiceProvider enum gains openLibrary member; factory method adds case to instantiate and return new OpenLibraryApi when provider is openLibrary.
HTTP error handling and Google Books improvements
src/apis/base_api.ts, src/apis/google_books_api.ts
apiGet now sends Accept: application/json header and explicitly handles HTTP 429 (rate limit), 403 (forbidden), and other 4xx errors. GoogleBooksApi detects ISBN queries via regex pattern, omits langRestrict for ISBN searches, extracts locale from settings rather than moment.js, and refactors cover URL logic into reusable extractCoverUrls helper with improved typing.
Open Library API models and implementation
src/apis/models/open_library_response.ts, src/apis/open_library_api.ts
New OpenLibrarySearchResponse and OpenLibraryDoc interfaces define the Open Library API contract. OpenLibraryApi implements getByQuery to search Open Library and createBookItem to transform response docs into the app's Book model, extracting ISBNs, constructing cover URLs, selecting publish year with fallback, and assembling metadata.
Settings UI wiring
src/settings/settings.ts
Settings adds Open Library option to service provider dropdown, updates toggleServiceProviderExtraSettings to conditionally hide extra button and cover-image edge-curl toggle for Open Library (while showing locale), imports GoogleBooksApi to enable live API key testing with success/failure notices.
Build and release configuration
.npmrc, pnpm-workspace.yaml, manifest.json, package.json
NPM config adds esbuild to onlyBuiltDependencies; new pnpm-workspace.yaml enables esbuild workspace builds; version bumped to 0.8.0 across manifest and package metadata.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A library opens wide,
Books from Open search reside,
ISBN patterns caught with care,
Settings wrapped and wired fair,
Version bumped to 0.8's delight! 📚✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main changes: API overhaul for Google Books (addressing specific HTTP errors) and addition of Open Library provider.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Fix Google Books API errors and add Open Library provider

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Fix 429/403 rate-limit errors with explicit HTTP status handling
• Fix langRestrict bug: normalize locales, skip on default locale
• Fix ISBN search: use isbn: prefix, strip dashes, skip langRestrict
• Add Open Library provider: free, no API key, global coverage
• Add medium/large cover URLs via Google Books zoom parameter
• Improve API key test: performs real search instead of string check
Diagram
flowchart LR
  A["HTTP Requests"] -->|"throw: false + status checks"| B["Error Handling"]
  B -->|"429/403/4xx"| C["User-Friendly Messages"]
  D["Query Processing"] -->|"normalize locale"| E["Google Books API"]
  D -->|"ISBN detection"| F["ISBN Search"]
  E -->|"zoom parameter"| G["Cover URLs S/M/L"]
  H["Open Library API"] -->|"no key needed"| I["Alternative Provider"]
  J["Settings"] -->|"real search test"| K["API Key Validation"]

Loading

Grey Divider

File Changes

1. src/apis/base_api.ts Error handling +17/-1

Add error handling and Open Library import

src/apis/base_api.ts


2. src/apis/google_books_api.ts 🐞 Bug fix +37/-10

Fix langRestrict, ISBN search, cover URLs

src/apis/google_books_api.ts


3. src/apis/models/open_library_response.ts ✨ Enhancement +19/-0

Define Open Library API response types

src/apis/models/open_library_response.ts


View more (7)
4. src/apis/open_library_api.ts ✨ Enhancement +68/-0

Implement Open Library search provider

src/apis/open_library_api.ts


5. src/constants.ts ✨ Enhancement +1/-0

Add Open Library service provider enum

src/constants.ts


6. src/settings/settings.ts ✨ Enhancement +26/-9

Add Open Library UI and improve API testing

src/settings/settings.ts


7. .npmrc ⚙️ Configuration changes +2/-1

Configure esbuild dependency handling

.npmrc


8. manifest.json ⚙️ Configuration changes +1/-1

Bump version to 0.8.0

manifest.json


9. package.json ⚙️ Configuration changes +1/-1

Bump version to 0.8.0

package.json


10. pnpm-workspace.yaml ⚙️ Configuration changes +2/-0

Enable esbuild for fresh checkouts

pnpm-workspace.yaml


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Jun 4, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (2)

Context used

Grey Divider


Action required

1. apiGet() missing 429 cooldown 📎 Requirement gap ☼ Reliability
Description
The new 429 handling throws immediately without any backoff/cooldown, so repeated searches will
continue failing under persistent rate limiting. This does not provide a recovery path across
sessions/devices as required.
Code

src/apis/base_api.ts[R59-63]

+  if (res.status === 429) {
+    throw new Error(
+      'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
+    );
+  }
Evidence
PR Compliance ID 2 requires cooldown/backoff when repeated 429s occur. The added 429 handling in
apiGet() simply throws an error on res.status === 429 and does not implement any
throttling/backoff state or recovery timing.

Prevent persistent 429 errors from making all searches fail across devices
src/apis/base_api.ts[59-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`apiGet()` detects HTTP 429 but immediately throws an error and does not implement any cooldown/backoff. This means users who hit rate limits can keep failing on every subsequent search with no mitigation.

## Issue Context
Compliance requires reducing request frequency and/or using backoff/cooldown so searches can resume after waiting, with consistent behavior across sessions/devices.

## Fix Focus Areas
- src/apis/base_api.ts[40-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. 429 error lacks context 📎 Requirement gap ◔ Observability
Description
The thrown 429 error message does not include the 429 status code or any operation context (search
vs note creation), reducing the usefulness of console diagnostics. This makes troubleshooting
rate-limit incidents harder than required.
Code

src/apis/base_api.ts[R59-63]

+  if (res.status === 429) {
+    throw new Error(
+      'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
+    );
+  }
Evidence
PR Compliance ID 3 requires 429 diagnostics to include the operation context and status code. In the
new 429 branch, apiGet() throws a message that says "Rate limit reached" but does not explicitly
include 429 or any operation identifier.

Surface actionable diagnostics when requests fail with 429 in Obsidian console
src/apis/base_api.ts[59-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
For HTTP 429, the code throws an `Error` message that does not include the explicit status code (`429`) or the operation context (e.g., search vs note creation). The compliance requirement asks for actionable diagnostics in the Obsidian console.

## Issue Context
To meet the requirement, logs (and/or propagated errors) should include: operation name, status code, and a human-readable explanation.

## Fix Focus Areas
- src/apis/base_api.ts[59-63]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Misleading 429/403 errors 🐞 Bug ≡ Correctness
Description
apiGet() throws Google Books-specific guidance for HTTP 429/403, but apiGet() is also used by
NaverBooksApi and OpenLibraryApi, so users can see incorrect instructions (e.g., told to check
Google API key while using Naver/Open Library). This degrades troubleshooting and can hide the real
fix for the active provider.
Code

src/apis/base_api.ts[R59-66]

+  if (res.status === 429) {
+    throw new Error(
+      'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
+    );
+  }
+  if (res.status === 403) {
+    throw new Error('Access denied (403). Check your Google Books API key in Settings → Google API Settings.');
+  }
Evidence
The shared apiGet() hardcodes Google Books remediation text for 429/403. Both OpenLibraryApi and
NaverBooksApi call apiGet(), so the same Google-specific messages will be thrown for those
providers’ requests as well.

src/apis/base_api.ts[40-72]
src/apis/open_library_api.ts[13-35]
src/apis/naver_books_api.ts[11-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`apiGet()` is a shared HTTP helper used by multiple providers (Google/Naver/OpenLibrary). It currently throws Google Books-specific error messages for `429` and `403`, which becomes incorrect/misleading when the failing request belongs to Naver or Open Library.

## Issue Context
- `apiGet()` is used by GoogleBooksApi, NaverBooksApi, and OpenLibraryApi.
- Provider-specific remediation (e.g., “check Google API key”) should not live in a shared transport helper.

## Fix Focus Areas
- Move provider-specific messaging out of `apiGet()` (keep `apiGet()` generic, e.g., `Request failed, status X`, optionally including URL/response text).
- Implement provider-specific error mapping in each provider (e.g., GoogleBooksApi can translate `429/403` into Google-specific guidance; Naver can mention client id/secret).

### Code locations
- src/apis/base_api.ts[40-72]
- src/apis/naver_books_api.ts[11-35]
- src/apis/open_library_api.ts[13-36]
- src/apis/google_books_api.ts[48-58]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds Open Library as an additional book search provider and improves Google Books API handling, including better key testing and error handling.

Changes:

  • Introduce openLibrary as a new ServiceProvider and add an OpenLibraryApi implementation.
  • Improve Google Books search parameter building (ISBN handling, locale normalization) and add a real “Test API Key” flow in settings.
  • Harden apiGet error handling and add pnpm build allowlisting for esbuild.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/settings/settings.ts Adds Open Library option and implements a real Google Books API key test in settings UI.
src/constants.ts Introduces ServiceProvider.openLibrary.
src/apis/open_library_api.ts New Open Library provider implementation backed by /search.json.
src/apis/models/open_library_response.ts Adds type definitions for Open Library search results.
src/apis/google_books_api.ts Improves ISBN detection, locale normalization, and cover URL extraction helpers.
src/apis/base_api.ts Adds Open Library to factory and centralizes HTTP status handling for API calls.
pnpm-workspace.yaml Adds pnpm build allowlist configuration.
package.json Bumps package version to 0.8.0.
manifest.json Bumps plugin manifest version to 0.8.0.
.npmrc Adds onlyBuiltDependencies allowlist entry for esbuild.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/apis/base_api.ts
Comment on lines +59 to +69
if (res.status === 429) {
throw new Error(
'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
);
}
if (res.status === 403) {
throw new Error('Access denied (403). Check your Google Books API key in Settings → Google API Settings.');
}
if (res.status >= 400) {
throw new Error(`Request failed, status ${res.status}`);
}
Comment thread pnpm-workspace.yaml
Comment on lines +1 to +2
allowBuilds:
esbuild: true
Comment thread src/constants.ts
Comment on lines 1 to 5
export enum ServiceProvider {
google = 'google',
naver = 'naver',
openLibrary = 'openLibrary',
}
import { Book } from '@models/book.model';
import { OpenLibraryDoc, OpenLibrarySearchResponse } from './models/open_library_response';

const ISBN_RE = /^(97[89])?\d{9}[\dX]$/i;

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/apis/base_api.ts`:
- Around line 59-66: The 429/403 error messages in apiGet are hard-coded for
Google and therefore show incorrect remediation for other providers; update the
apiGet implementation to generate provider-aware messages (use the function's
provider/source parameter or derive the provider from the request URL) and
branch on possible values (e.g., "google", "naver", "openlibrary") so 429 errors
advise adding/configuring a key only for Google/Naver and 403 errors reference
the correct provider-specific settings (Google API Settings vs Naver API
settings) or mention that Open Library requires no key; replace the current
throw new Error(...) calls for res.status === 429 and res.status === 403 with
messages selected based on that provider detection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 369b4366-bcc0-4d74-8079-3b1fa29bef35

📥 Commits

Reviewing files that changed from the base of the PR and between 9bd8d0c and 4fd793c.

📒 Files selected for processing (10)
  • .npmrc
  • manifest.json
  • package.json
  • pnpm-workspace.yaml
  • src/apis/base_api.ts
  • src/apis/google_books_api.ts
  • src/apis/models/open_library_response.ts
  • src/apis/open_library_api.ts
  • src/constants.ts
  • src/settings/settings.ts

Comment thread src/apis/base_api.ts
Comment on lines +59 to +66
if (res.status === 429) {
throw new Error(
'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
);
}
if (res.status === 403) {
throw new Error('Access denied (403). Check your Google Books API key in Settings → Google API Settings.');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make shared 429/403 errors provider-aware.

At Line 59 and Line 65, apiGet always tells users to fix Google API settings. This function is shared by Google, Naver, and Open Library, so non-Google failures will show incorrect remediation.

Suggested fix
-  if (res.status === 429) {
-    throw new Error(
-      'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
-    );
-  }
-  if (res.status === 403) {
-    throw new Error('Access denied (403). Check your Google Books API key in Settings → Google API Settings.');
-  }
+  if (res.status === 429) {
+    if (apiURL.hostname.includes('googleapis.com')) {
+      throw new Error(
+        'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
+      );
+    }
+    throw new Error(`Rate limit reached (429) from ${apiURL.hostname}.`);
+  }
+  if (res.status === 403) {
+    if (apiURL.hostname.includes('googleapis.com')) {
+      throw new Error('Access denied (403). Check your Google Books API key in Settings → Google API Settings.');
+    }
+    throw new Error(`Access denied (403) from ${apiURL.hostname}.`);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (res.status === 429) {
throw new Error(
'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
);
}
if (res.status === 403) {
throw new Error('Access denied (403). Check your Google Books API key in Settings → Google API Settings.');
}
if (res.status === 429) {
if (apiURL.hostname.includes('googleapis.com')) {
throw new Error(
'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
);
}
throw new Error(`Rate limit reached (429) from ${apiURL.hostname}.`);
}
if (res.status === 403) {
if (apiURL.hostname.includes('googleapis.com')) {
throw new Error('Access denied (403). Check your Google Books API key in Settings → Google API Settings.');
}
throw new Error(`Access denied (403) from ${apiURL.hostname}.`);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/apis/base_api.ts` around lines 59 - 66, The 429/403 error messages in
apiGet are hard-coded for Google and therefore show incorrect remediation for
other providers; update the apiGet implementation to generate provider-aware
messages (use the function's provider/source parameter or derive the provider
from the request URL) and branch on possible values (e.g., "google", "naver",
"openlibrary") so 429 errors advise adding/configuring a key only for
Google/Naver and 403 errors reference the correct provider-specific settings
(Google API Settings vs Naver API settings) or mention that Open Library
requires no key; replace the current throw new Error(...) calls for res.status
=== 429 and res.status === 403 with messages selected based on that provider
detection.

Comment thread src/apis/base_api.ts
Comment on lines +59 to +63
if (res.status === 429) {
throw new Error(
'Rate limit reached. Add a Google Books API key in Settings → Google API Settings, or switch to Open Library (no key needed).',
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. apiget() missing 429 cooldown 📎 Requirement gap ☼ Reliability

The new 429 handling throws immediately without any backoff/cooldown, so repeated searches will
continue failing under persistent rate limiting. This does not provide a recovery path across
sessions/devices as required.
Agent Prompt
## Issue description
`apiGet()` detects HTTP 429 but immediately throws an error and does not implement any cooldown/backoff. This means users who hit rate limits can keep failing on every subsequent search with no mitigation.

## Issue Context
Compliance requires reducing request frequency and/or using backoff/cooldown so searches can resume after waiting, with consistent behavior across sessions/devices.

## Fix Focus Areas
- src/apis/base_api.ts[40-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error: Request failed, status 429

2 participants