unofficial-hardcover 0.1.1
Unofficial Python wrapper for the Hardcover API.
Requires Python: >=3.9

[![Version](https://img.shields.io/pypi/v/unofficial-hardcover?label=version)](https://pypi.org/project/unofficial-hardcover/)
[![codecov](https://codecov.io/github/fiachracurran/unofficial-hardcover/graph/badge.svg?token=YULOELNRZA)](https://codecov.io/github/fiachracurran/unofficial-hardcover)
[![License](https://img.shields.io/github/license/fiachracurran/unofficial-hardcover)](./LICENSE)
[![Changelog](https://img.shields.io/badge/changelog-md-blue)](./CHANGELOG.md)

# unofficial-hardcover

Unofficial async Python client for the Hardcover GraphQL API.

• Async-first using `httpx`
• Dict and typed model returns (dataclass `Book`, `User`, `UserBook`, `Author`)
• Helpful convenience methods and raw GraphQL access
• Solid error handling and logging

## Installation

Runtime only:

```bash
pip install unofficial-hardcover
```

From source (dev mode with extras):

```bash
pip install -e .[dev]
```

## Quickstart

Set your token (or create a `.env` file):

```bash
export HARDCOVER_API_TOKEN="your_token_here"
```

Basic usage:

```python
import asyncio
from unofficial_hardcover.client import HardcoverClient

async def main():
	async with HardcoverClient.from_env() as client:
		me = await client.get_me()
		print(me)

asyncio.run(main())
```

Typed helpers:

```python
user = await client.get_me_model()  # -> Optional[User]
book = await client.get_book_model("1")  # -> Optional[Book]
user_books = await client.get_user_books_models()  # -> List[UserBook]
author = await client.get_author_model("42")  # -> Optional[Author]
```

## Return types: dict vs models

This client intentionally supports both raw-dict responses and typed dataclass models.

- Raw dict methods: maximum flexibility and forward-compatibility with the GraphQL schema. Names end without a suffix, e.g. `get_me`, `get_book`.
- Typed model helpers: ergonomic, type-checked Python objects. Names end with `_model` or `_models`, e.g. `get_me_model`, `get_book_model`, `get_user_books_models`.
- Typed model helpers: ergonomic, type-checked Python objects. Names end with `_model` or `_models`, e.g. `get_me_model`, `get_book_model`, `get_user_books_models`, `get_author_model`.

Pick whichever suits your project. In new code, we recommend the typed helpers by default, while keeping dict methods for power users and advanced/custom queries.

Side-by-side usage example:

```python
# Raw dicts
me = await client.get_me()                # dict | None
book = await client.get_book(1)           # dict | None
author = await client.get_author("42")     # dict | None

# Typed models
user = await client.get_me_model()                 # Optional[User]
maybe_book = await client.get_book_model(1)        # Optional[Book]
user_books = await client.get_user_books_models()  # list[UserBook]
maybe_author = await client.get_author_model("42") # Optional[Author]

# Reading status enum convenience
for user_book in user_books:
	# status_id remains available; status is a helpful IntEnum mapping
	print(user_book.status_id, user_book.status.name)  # e.g., 2 READING
```

## Auth and environment

Authentication uses the `HARDCOVER_API_TOKEN` environment variable. The library reads it via `HardcoverClient.from_env()`.

- The examples load a local `.env` (via `python-dotenv`) for convenience.
- The library itself does not implicitly load `.env`; this avoids surprising production behavior. If you need that, load it in your app entry point.

Quick check:

```bash
echo "$HARDCOVER_API_TOKEN"   # should print a non-empty value
```

## Examples

There is a set of runnable examples showing common tasks in the examples dir:

- See [examples/README.md](./examples/README.md) for a full catalog

## Errors and Logging

The client raises:

- `HardcoverAPIError`: configuration issues (e.g., missing token)
- `HardcoverHTTPError`: HTTP errors (e.g., 401, 429, 5xx)
- `HardcoverGraphQLError`: GraphQL validation or execution errors

Enable verbose logging with `debug=True` or configure `logging` for deeper insights.

## Development

Run tests and linters:

```bash
pytest -q
black --check . && isort --check-only . && flake8 . && mypy
```

CI runs tests on Python 3.9–3.12 and enforces formatting, lint, and type checks.

See DEVELOPER.md for full developer setup and workflows.

## Contributing

Contributions are welcome!

- Start with [CONTRIBUTING.md](./CONTRIBUTING.md) for how to report issues and submit PRs.
- For local setup, tests, linting, and release workflow, see [DEVELOPER.md](./DEVELOPER.md).

## Disclaimer

This is an unofficial library and not affiliated with Hardcover. API surface is likely to change so this wrapper may break / not work as intended.