animedb-api 0.2.1

GraphQL API for the animedb Rust metadata catalog
Documentation

animedb

animedb is a Rust-first metadata project for local media servers.

Advisory: animedb stores and normalizes public metadata, but it does not override provider Terms of Service, attribution requirements, authentication rules, rate limits or mature-content restrictions. Before enabling bulk sync for a source, verify that your intended usage is allowed by that source and configure conservative sync budgets.

It has two consumption modes that can be used separately or together:

  • local-first: manage a local SQLite catalog with schema, downloads, sync, FTS5 search and JSON source payloads
  • remote-first: query normalized metadata from remote providers without forcing client applications to manage persistence or provider-specific normalization

The project also ships a Rust GraphQL API on top of the same crate.

See REFERENCE.md for the current library and API reference.

Supported providers

Provider Media kinds Data source Licensed under
AniList Anime, Manga GraphQL API AniList Terms
Jikan (MyAnimeList) Anime, Manga REST API Jikan MIT
Kitsu Anime, Manga REST API Kitsu API Policy
TVmaze Shows REST API (api.tvmaze.com) CC BY-SA 4.0
IMDb Movies, Shows Official TSV datasets (datasets.imdb.com) IMDb Conditions

Workspace

  • crates/animedb — library crate with SQLite schema management, sync and query APIs
  • crates/animedb-api — GraphQL API binary built on top of animedb

Feature flags

# Full featured (local SQLite + all providers) — default
animedb = "0.2"

# Remote-only, no SQLite dependency (safe for sqlx-based projects)
animedb = { version = "0.2", default-features = false, features = ["remote"] }
  • local-db (default): local SQLite storage, sync state persistence, and the [AnimeDb] type. This feature pulls in rusqlite with a bundled SQLite.
  • remote (default): remote provider clients and the normalized data model. Zero native dependencies.

Why feature gates? local-db requires rusqlite (bundled SQLite). Many Rust projects already use sqlx with its own SQLite linkage, and Cargo rejects putting both in the same dependency graph. If your project uses sqlx, depend on animedb with only features = ["remote"] to get all provider clients, normalization types, and sync data structures without any SQLite conflict.

Current Rust surface

Local-first

use animedb::{AnimeDb, SourceName};

let (db, report) = AnimeDb::generate_database_with_report("/tmp/animedb.sqlite")?;
println!("downloaded {} records", report.total_upserted_records);

let updated = AnimeDb::sync_database("/tmp/animedb.sqlite")?;
println!("synced {} records", updated.total_upserted_records);

let monster = db.anime_metadata().by_external_id(SourceName::AniList, "19")?;
println!("{}", monster.name());

let show = db.show_metadata().search("breaking bad")?;
let show = db.get_by_external_id(SourceName::Imdb, "tt0903747")?;

let movies = db.movie_metadata().search("spirited away")?;
println!("movie hits: {}", movies.len());

# Ok::<(), animedb::Error>(())

Remote-first

use animedb::AnimeDb;

let remote = AnimeDb::remote_anilist();
let results = remote.anime_metadata().search("monster")?;
let media = remote.anime_metadata().by_id("19")?;
# Ok::<(), animedb::Error>(())

Or choose the provider dynamically:

use animedb::{AnimeDb, RemoteSource};

let remote = AnimeDb::remote(RemoteSource::Tvmaze);
let results = remote.show_metadata().search("breaking bad")?;
# Ok::<(), animedb::Error>(())

Media kinds

All providers map to one of four supported kinds:

pub enum MediaKind {
    Anime,  // Japanese animation
    Manga,  // Japanese comics
    Show,   // TV series (from TVmaze / IMDb)
    Movie,  // Films (from IMDb)
}

SQLite schema

The SQLite catalog is created and migrated by the crate itself. The current schema includes:

  • media — canonical normalized records
  • media_alias — normalized aliases and synonyms
  • media_external_id — source-specific identifiers
  • source_record — raw per-source JSON payloads and source update metadata
  • field_provenance — winner-per-field audit trail for canonical merge decisions
  • sync_state — persisted sync checkpoints/cursors
  • media_ftsFTS5 index for title, alias and synopsis search

The connection is configured with:

  • PRAGMA journal_mode=WAL
  • PRAGMA synchronous=NORMAL
  • PRAGMA foreign_keys=ON
  • PRAGMA busy_timeout=5000
  • PRAGMA temp_store=MEMORY

GraphQL API

The GraphQL API is provided by animedb-api. Run it locally:

cargo run -p animedb-api

Environment variables:

  • ANIMEDB_DATABASE_PATH — SQLite file path, default /data/animedb.sqlite
  • ANIMEDB_LISTEN_ADDR — bind address, default 0.0.0.0:8080

Query example (search shows and movies):

{
  search(query: "breaking bad", options: { limit: 5, mediaKind: SHOW }) {
    mediaId
    titleDisplay
    mediaKind
    genres
    externalIds { source sourceId }
  }
}

Docker

Build and run the Rust GraphQL API:

docker build -t animedb .
docker run --rm -p 8080:8080 -v $(pwd)/data:/data animedb

Make targets

The repository includes a Makefile for common workflows:

  • make build — compile the workspace
  • make test — run the Rust test suite
  • make test-e2e — run the end-to-end integration test (scripts/e2e_test.sh)
  • make docker-build — build the API image
  • make docker-run — run the API image locally
  • make debug-api — run the GraphQL API directly with cargo run