文書 · Documentation
AniLink
A typed TypeScript client for the AniList GraphQL and MyAnimeList REST APIs. One class, two isolated providers, normalized errors, retries, pacing, and a generated operation reference.
One client, two providers
Typed from install to response
Construct once with per-provider credentials. Each API is fully typed, from the variables you send to the responses you get back. The two providers never share a transport credential.
import { AniLink } from "anilink-api-wrapper";
// One client, two isolated providers.
const aniLink = new AniLink({
anilist: { authToken: process.env.ANILIST_TOKEN },
mal: { accessToken: process.env.MAL_TOKEN },
});
// AniList: typed GraphQL, no token needed for public reads.
const anime = await aniLink.anilist.query.media({
id: 21,
type: "ANIME",
});
console.log(anime.media?.title?.romaji); // prints "One Piece"
// MyAnimeList: typed REST, public fields need no token.
const mal = await aniLink.mal.anime.get({ id: 21 }, {
fields: ["id", "title", "main_picture"],
});
console.log(mal.title);Why AniLink
The plumbing, done once
Calling AniList or MAL directly means hand-rolling HTTP, GraphQL documents, OAuth flows, retry logic, and rate-limit handling. AniLink does that once, with types.
Typed operations
Every query, page query, and mutation ships with typed variables and a typed response, generated from the provider schemas. You never write GraphQL shapes by hand.
Normalized errors
Provider failures become AniLinkError subclasses with stable code values. Classify failures by code, not by parsing message strings.
Resilience built in
Retries use jittered backoff. You can turn on rate-limit pacing and a circuit breaker. Both providers get the same behavior.
Provider isolation
Each provider slot has its own credentials and transport settings. A MAL token never reaches AniList, and vice versa.
Observability hooks
onRequestStart, onSuccess, onRetry, and onError fire at every stage of the request lifecycle. Instrument calls without wrapping a single method.
Cancellation & timeouts
Pass an AbortSignal or a per-request timeout. The transport cancels in flight, releases the attempt, and throws a typed cancellation error.
Two providers, one client
AniList & MyAnimeList
Both providers share a transport layer: timeouts, retries, pacing, the circuit breaker, hooks, and error normalization. They never share credentials.
| AniList GraphQL | MyAnimeList REST | |
|---|---|---|
| Protocol | GraphQL | REST |
| Namespace | aniLink.anilist | aniLink.mal |
| Operations | Queries · Page · Mutations · custom() | anime.get · seasonal · ranking · user.animeList · user.mangaList · user.me |
| Auth | OAuth bearer token | PKCE OAuth2 access token |
| Credential slot | anilist.authToken | mal.accessToken |
The documentation
Where to start reading
The docs form a single reading path. Begin with the start guides, move through the core transport concepts, then branch into provider-specific guides and the generated operation reference.
Start here
Getting oriented
Core concepts
The transport layer
- Error handlingNormalized error classes and stable codes.
- Retries & resilienceBackoff, pacing, and the circuit breaker.
- Cancellation & timeoutsAbort signals and per-request deadlines.
- Per-request optionsOverride transport behavior for a single call.
- ObservabilityLogging hooks and request telemetry.
- Response cacheOpt-in TTL caching for read-heavy traversals.
Cookbook
Patterns & recipes
AniList · GraphQL
AniList guides
MyAnimeList · REST
MAL guides
Ready to build?
Install the package and make your first typed call in under a minute.