# AniList Wrapper Documentation

This documentation is written for building with the wrapper quickly, then reaching for the API reference when you need exact method names and response shapes.

## Start Here

1. [Install the package](#install)
2. [Create a client](#create-a-client)
3. [Choose a service](#choose-a-service)
4. [Run an example](#examples)
5. [Configure the client](./client-options.md)
6. [Handle errors](./errors.md)
7. [Migrate selected queries](./selection-migration.md)
8. [Migrate from v3 to v4](./migrating-to-v4.md)
9. [Find contribution ideas](./contributing-ideas.md)

## Install

```bash
npm install @api-wrappers/anilist-wrapper
# or
bun add @api-wrappers/anilist-wrapper
# or
pnpm add @api-wrappers/anilist-wrapper
# or
yarn add @api-wrappers/anilist-wrapper
```

## Create A Client

Public AniList data does not need a token:

```typescript
import { Anilist } from "@api-wrappers/anilist-wrapper";

const anilist = new Anilist();
```

Authenticated reads and mutations use the same client with an access token:

```typescript
import { Anilist } from "@api-wrappers/anilist-wrapper";

const anilist = new Anilist(process.env.ANILIST_TOKEN);
```

The constructor also accepts options for a custom endpoint, headers, timeout,
retry policy, plugins, and transport. See the
[client options guide](./client-options.md) for the full list, and the
[error handling guide](./errors.md) for the typed errors and rate-limit
behavior.

Read the [authentication guide](./authentication.md) before using private user data, favorites, or list mutations.

## Read Response Shapes

The wrapper returns the typed AniList GraphQL response. That means the top-level field usually matches the operation:

```typescript
const anime = await anilist.anime.getAnimeById(16498);
console.log(anime.Media?.title?.userPreferred);

const search = await anilist.anime.getAnimeBySearch("Cowboy Bebop");
console.log(search.Page?.media?.[0]?.title?.userPreferred);

const list = await anilist.user.getUserAnimeListByUsername("example_user");
console.log(list.MediaListCollection?.lists?.[0]?.entries?.length);
```

Most fields are nullable because AniList marks many GraphQL fields as nullable. Prefer optional chaining or explicit null checks when rendering data.

## Select Response Fields

Every endpoint that supports selected fields accepts a root-object `select`
shape and returns the same normalized root:

```typescript
const { media } = await anilist.anime.getAnimeById(16498, {
	select: {
		media: {
			id: true,
			title: { userPreferred: true },
		},
	},
});

const { page } = await anilist.anime.getAnimeBySearch("Cowboy Bebop", 1, 10, {
	select: {
		page: {
			pageInfo: { currentPage: true, hasNextPage: true },
			media: { id: true, title: { userPreferred: true } },
		},
	},
});
```

Read the [selection guide](./selection-migration.md) for the full root map and
mutation shapes. Selections must use the root shape; v3 direct selections were
removed in v4.

## Choose A Service

| Service | Property | Best for |
| --- | --- | --- |
| Anime | `anilist.anime` | Anime details, search, trending, popular, browse and seasonal filters, genre filtering, relations, characters, staff, recommendations |
| Manga | `anilist.manga` | Manga details, search, trending, popular, browse filters, genre filtering, relations, characters, staff, recommendations |
| Character | `anilist.character` | Character details, birthday lists, favorite mutations |
| Staff | `anilist.staff` | Staff details, birthday lists, favorite mutations |
| Studio | `anilist.studio` | Studio details, search, favorite mutations |
| User | `anilist.user` | User profiles, the authenticated viewer, statistics, reviews, anime lists, manga lists |
| Media | `anilist.media` | Generic media records, list access by media type, genres, tags, airing schedules, reviews, recommendations |
| Media List | `anilist.mediaList` | Media list entries, saving progress, bulk updates, deleting entries and custom lists |
| Social | `anilist.social` | Follows, notifications, activities, forum threads and comments, likes, site statistics |
| GraphQL | `anilist.graphql` | Any AniList query or mutation |

## Common Patterns

### Pagination

List methods accept `page` and `perPage` arguments. Defaults are documented per
method, and AniList caps `perPage` at 50.

```typescript
const pageTwo = await anilist.anime.getTrendingAnime(2, 20);
```

Collect every page sequentially with `collectPages`, or stream items with
`paginate`:

```typescript
import { collectPages } from "@api-wrappers/anilist-wrapper";

const media = await collectPages(
	(page) => anilist.anime.getAnimeBySearch("Frieren", page, 50),
	(response) => ({
		pageInfo: response.Page?.pageInfo,
		items: response.Page?.media,
	}),
);
```

### Anime and manga naming

The anime and manga services cover the same concepts with different prefixes.
Manga sub-resource methods carry the `Manga` prefix; the anime equivalents do
not.

| Anime | Manga |
| --- | --- |
| `getAnimeById` | `getMangaById` |
| `getAnimeByTitle` | `getMangaByTitle` |
| `getAnimeBySearch` | `getMangaBySearch` |
| `getTrendingAnime` | `getMangaTrending` |
| `getPopularAnime` | `getMangaPopular` |
| `browseAnime` | `browseManga` |
| `getAnimeListByGenre` | `getMangaListByGenre` |
| `getRecommendations` | `getMangaRecommendations` |
| `getRelations` | `getMangaRelations` |
| `getCharacters` | `getMangaCharacters` |
| `getStaff` | `getMangaStaff` |

### Media list statuses

Use the generated enum when filtering or saving list entries:

```typescript
import { MediaListStatus } from "@api-wrappers/anilist-wrapper";

const completed = await anilist.user.getUserAnimeListByUsername(
	"example_user",
	MediaListStatus.Completed,
);
```

### Raw GraphQL

Use raw GraphQL when AniList supports a field that does not have a convenience method yet.

```typescript
import { gql } from "@api-wrappers/anilist-wrapper";

const data = await anilist.graphql.request<{
	SiteStatistics: {
		users: { count: number | null } | null;
	} | null;
}>(gql`
	query SiteStats {
		SiteStatistics {
			users {
				count
			}
		}
	}
`);
```

## API Reference

- [Anime](./api/anime.md)
- [Manga](./api/manga.md)
- [Characters](./api/character.md)
- [Staff](./api/staff.md)
- [Studios](./api/studio.md)
- [Users](./api/user.md)
- [Media](./api/media.md)
- [Media Lists](./api/media-list.md)
- [Social](./api/social.md)
- [Raw GraphQL](./api/graphql.md)
- [Client Options](./client-options.md)
- [Error Handling](./errors.md)
- [Selection Migration](./selection-migration.md)
- [Migrating to v4](./migrating-to-v4.md)

## Examples

For copyable snippets, read [Practical examples](./examples.md).

The [`examples/`](../examples/) directory contains runnable scripts:

```bash
bun examples/basic-anime.ts
bun examples/manga-workflow.ts
bun examples/characters-and-staff.ts
bun examples/raw-graphql.ts
ANILIST_TOKEN=... ANILIST_USERNAME=... bun examples/authenticated-user.ts
```

## Generated GraphQL Types

Generated GraphQL schema and operation types live in `src/__generated__/`.
They are produced from `codegen.yml`, documents in `src/queries/`, fragments in
`src/fragments/`, and AniList's remote schema.

Run `bun run codegen` only when GraphQL operations, fragments, or generated
types need to be refreshed. The generated SDK is patched by
`scripts/patch-codegen.ts` to use the package's `@api-wrappers/api-core`
client shape.
