> ## Documentation Index
> Fetch the complete documentation index at: https://devperez08-platform-list-anime-54.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Anime Discovery

> Discover new anime through search, trending lists, and curated recommendations powered by Jikan API

## Overview

EpiNeko's discovery system helps users find their next favorite anime through powerful search capabilities and curated trending lists. All anime data is fetched from the Jikan API (unofficial MyAnimeList API), providing access to a vast database of anime information.

<CardGroup cols={2}>
  <Card title="Real-time Search" icon="magnifying-glass">
    Instant search results as you type with debouncing
  </Card>

  <Card title="Top Anime" icon="trophy">
    Browse the highest-rated anime on MyAnimeList
  </Card>

  <Card title="Rich Metadata" icon="database">
    Access titles, scores, synopses, images, and more
  </Card>

  <Card title="Pagination" icon="arrow-right-arrow-left">
    Navigate through extensive anime catalogs
  </Card>
</CardGroup>

## Jikan API Integration

EpiNeko uses the Jikan v4 API to fetch anime data from MyAnimeList:

```typescript theme={null}
const JIKAN_API_BASE = 'https://api.jikan.moe/v4';

// Generic fetch function with caching
async function jikanFetch<T>(endpoint: string, revalidate = 3600): Promise<T> {
  const response = await fetch(`${JIKAN_API_BASE}${endpoint}`, {
    next: { revalidate: revalidate }
  });

  if (!response.ok) {
    if (response.status === 429) {
      throw new Error('Jikan API rate limit exceeded. Please try again later.');
    }
    throw new Error(`Jikan API error: ${response.statusText}`);
  }

  return response.json();
}
```

**Key Features** (src/services/jikan.ts:43-56):

* Built-in rate limit handling (429 errors)
* Next.js cache revalidation for performance
* Type-safe responses with TypeScript interfaces
* Automatic error handling

## Data Structures

### Anime Object

The complete anime data structure returned by Jikan:

```typescript theme={null}
export interface JikanAnime {
  mal_id: number;                      // MyAnimeList ID (primary identifier)
  url: string;                         // MAL URL
  images: {
    jpg: JikanImage;
    webp: JikanImage;
  };
  title: string;                       // Romaji title
  title_english: string | null;        // English title
  title_japanese: string | null;       // Japanese title
  type: string;                        // TV, Movie, OVA, etc.
  episodes: number | null;             // Total episode count
  status: string;                      // Airing status
  score: number | null;                // MAL score (0-10)
  synopsis: string | null;             // Description
  background: string | null;           // Additional context
  season: string | null;               // Spring, Summer, Fall, Winter
  year: number | null;                 // Release year
}
```

### Image Object

Multiple image sizes for optimal performance:

```typescript theme={null}
export interface JikanImage {
  image_url: string;          // Default size
  small_image_url: string;    // Thumbnail
  large_image_url: string;    // High resolution
}
```

### Paginated Response

All list endpoints return paginated data:

```typescript theme={null}
export interface JikanResponse<T> {
  data: T;
  pagination?: {
    last_visible_page: number;
    has_next_page: boolean;
    current_page: number;
    items: {
      count: number;      // Items on current page
      total: number;      // Total items
      per_page: number;   // Items per page
    };
  };
}
```

## Core API Functions

### Search Anime

Search for anime by title with pagination support:

```typescript theme={null}
import { searchAnime } from '@/services/jikan';

// Basic search
const results = await searchAnime('naruto');
console.log(results.data); // Array of matching anime

// With pagination
const page2 = await searchAnime('naruto', 2);

// Access pagination info
if (results.pagination?.has_next_page) {
  console.log(`Page ${results.pagination.current_page} of ${results.pagination.last_visible_page}`);
}
```

**Function Signature** (src/services/jikan.ts:62-64):

```typescript theme={null}
export const searchAnime = async (
  query: string, 
  page = 1
): Promise<JikanResponse<JikanAnime[]>>
```

<Note>
  Search queries are automatically URL-encoded. Results are cached for 1 hour (3600 seconds) to reduce API calls.
</Note>

### Get Top Anime

Retrieve the highest-rated anime from MyAnimeList:

```typescript theme={null}
import { getTopAnime } from '@/services/jikan';

// Get first page of top anime
const topAnime = await getTopAnime();

// Display on homepage
topAnime.data.forEach(anime => {
  console.log(`${anime.title} - ★${anime.score}`);
});

// Load more pages
const nextPage = await getTopAnime(2);
```

**Implementation** (src/services/jikan.ts:58-60):

* Returns 25 anime per page
* Sorted by MAL score
* Cached for 1 hour

### Get Anime Details

Fetch complete information for a specific anime:

```typescript theme={null}
import { getAnimeById } from '@/services/jikan';

// Get by MyAnimeList ID
const anime = await getAnimeById(52991);

console.log(anime.data.title);        // "Sousou no Frieren"
console.log(anime.data.episodes);     // 28
console.log(anime.data.score);        // 9.39
console.log(anime.data.synopsis);     // Full description
```

**Caching** (src/services/jikan.ts:66-68):

* Details are cached for 24 hours (86400 seconds)
* Longer cache duration since anime details rarely change

### Get Anime Characters

Retrieve character information for an anime:

```typescript theme={null}
import { getAnimeCharacters } from '@/services/jikan';

const characters = await getAnimeCharacters(52991);

characters.data.forEach(char => {
  console.log(char.character.name);
  console.log(char.role); // Main, Supporting
  console.log(char.voice_actors);
});
```

### Get Anime Episodes

Fetch episode lists with titles and metadata:

```typescript theme={null}
import { getAnimeEpisodes } from '@/services/jikan';

const episodes = await getAnimeEpisodes(52991);

episodes.data.forEach(ep => {
  console.log(`Episode ${ep.mal_id}: ${ep.title}`);
  console.log(`Aired: ${ep.aired}`);
  console.log(`Filler: ${ep.filler}`);
  console.log(`Recap: ${ep.recap}`);
});
```

**Episode Structure** (src/services/jikan.ts:74-85):

```typescript theme={null}
export interface JikanEpisode {
  mal_id: number;
  url: string;
  title: string;
  title_japanese: string | null;
  title_romanji: string | null;
  duration: number | null;
  aired: string | null;
  filler: boolean;
  recap: boolean;
  forum_url: string | null;
}
```

## UI Components

### SearchBar Component

Real-time search with autocomplete dropdown:

```tsx theme={null}
import SearchBar from '@/components/anime/SearchBar';

// Add to navigation or header
<SearchBar />
```

<Steps>
  <Step title="User Types Query">
    Input is debounced by 500ms to avoid excessive API calls
  </Step>

  <Step title="Minimum Length">
    Search triggers when query length exceeds 2 characters
  </Step>

  <Step title="API Call">
    Fetches top 5 matching results from Jikan
  </Step>

  <Step title="Display Results">
    Shows anime cards with thumbnail, title, type, and score
  </Step>

  <Step title="Click Result">
    Navigates to anime detail page or closes dropdown
  </Step>
</Steps>

**Key Features** (src/components/anime/SearchBar.tsx):

* Click-outside detection to close dropdown
* Loading spinner during search
* "No results" message when appropriate
* "View all results" link for full search page

### AnimeCard Component

Reusable card component for displaying anime in grids:

```tsx theme={null}
import AnimeCard from '@/components/anime/AnimeCard';

<AnimeCard 
  mal_id={52991}
  image="https://cdn.myanimelist.net/images/anime/..."
  title="Sousou no Frieren"
  score="9.39"
/>
```

**Visual Effects** (src/components/anime/AnimeCard.tsx:14-46):

* Hover scale animation (1.02x)
* Image zoom effect on hover
* Primary color glow border
* Rating badge overlay
* Gradient overlay on hover
* Fallback placeholder if image fails

### Homepage Discovery

The homepage showcases trending anime with a hero section:

<Tabs>
  <Tab title="Hero Section">
    ```tsx theme={null}
    // Features the #1 trending anime
    // Large background image with gradient overlays
    // Title, synopsis, and CTA buttons
    // Auto-populated from getTopAnime()
    ```

    Located at src/app/page.tsx:31-79
  </Tab>

  <Tab title="Trending Carousel">
    ```tsx theme={null}
    // Horizontal scrollable list
    // Displays top anime from Jikan
    // Snap scrolling on mobile
    // Click to view details
    ```

    Located at src/app/page.tsx:84-121
  </Tab>
</Tabs>

## Caching Strategy

EpiNeko implements intelligent caching to balance freshness with performance:

<CardGroup cols={3}>
  <Card title="Search Results" icon="clock">
    **1 hour cache**

    Balances freshness with reduced API load for popular searches
  </Card>

  <Card title="Top Anime" icon="clock">
    **1 hour cache**

    Rankings change slowly, hourly updates are sufficient
  </Card>

  <Card title="Anime Details" icon="clock">
    **24 hour cache**

    Individual anime data is static, rarely needs updating
  </Card>
</CardGroup>

## Rate Limiting

The Jikan API has rate limits to protect MyAnimeList servers:

<Warning>
  **Rate Limit: 60 requests per minute, 3 requests per second**

  EpiNeko automatically detects 429 errors and displays user-friendly messages. Implement client-side throttling for heavy usage patterns.
</Warning>

```typescript theme={null}
// Rate limit handling (src/services/jikan.ts:49-52)
if (response.status === 429) {
  throw new Error('Jikan API rate limit exceeded. Please try again later.');
}
```

**Best Practices:**

* Use cached data when possible
* Debounce search inputs (500ms minimum)
* Implement loading states during API calls
* Queue requests if making multiple calls
* Consider implementing request retry logic with exponential backoff

## Error Handling

Robust error handling for network and API issues:

<CodeGroup>
  ```typescript Network Errors theme={null}
  try {
    const results = await searchAnime('naruto');
  } catch (error) {
    if (error.message.includes('rate limit')) {
      // Display rate limit message
      alert('Too many requests. Please wait a moment.');
    } else if (error.message.includes('API error')) {
      // API returned an error status
      alert('Service temporarily unavailable');
    } else {
      // Network error
      alert('Connection failed. Check your internet.');
    }
  }
  ```

  ```typescript Empty Results theme={null}
  const results = await searchAnime('xyz123nonexistent');

  if (results.data.length === 0) {
    console.log('No anime found matching your search');
    // Display empty state UI
  }
  ```

  ```typescript Null Values theme={null}
  const anime = await getAnimeById(12345);

  // Some fields may be null
  const episodeCount = anime.data.episodes ?? 'Unknown';
  const synopsis = anime.data.synopsis ?? 'No description available';
  const englishTitle = anime.data.title_english ?? anime.data.title;
  ```
</CodeGroup>

## Search Optimization

Tips for implementing efficient search:

<Steps>
  <Step title="Debounce Input">
    Wait 300-500ms after user stops typing before triggering search

    ```typescript theme={null}
    const timer = setTimeout(async () => {
      if (query.length > 2) {
        await searchAnime(query);
      }
    }, 500);
    ```
  </Step>

  <Step title="Limit Results">
    Only show top 5-10 results in autocomplete dropdowns

    ```typescript theme={null}
    const results = await searchAnime(query);
    setDisplayed(results.data.slice(0, 5));
    ```
  </Step>

  <Step title="Cancel Previous">
    Abort in-flight requests when new search starts

    ```typescript theme={null}
    const controller = new AbortController();
    // Cancel on new search
    ```
  </Step>

  <Step title="Show Loading">
    Display loading indicators during API calls for better UX
  </Step>
</Steps>

## Related Features

* [Library Management](/features/library-management) - Save discovered anime
* [Anime Details Pages](/integration/jikan-api) - View complete information
* [Search Implementation](/development/components) - Build custom search
