> ## 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.

# Supabase Integration

> Complete guide to using Supabase for authentication and database operations in EpiNeko

## Overview

EpiNeko uses [Supabase](https://supabase.com/) for authentication, database management, and Row Level Security (RLS). The integration provides separate client and server utilities for optimal performance and security.

<Note>
  Supabase utilities are located in `src/utils/supabase/` with separate implementations for client-side, server-side, and middleware usage.
</Note>

## Environment Variables

Add these environment variables to your `.env.local` file:

```bash theme={null}
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
```

<Warning>
  Both environment variables are required. The application will throw an error if they are missing.
</Warning>

## Client-Side Usage

Use the client-side Supabase client for browser-based operations in Client Components.

### Creating a Client

```typescript theme={null}
// src/utils/supabase/client.ts:3
import { createClient } from '@/utils/supabase/client';

const supabase = createClient();
```

### Implementation Details

```typescript theme={null}
// src/utils/supabase/client.ts:1
import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
  const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;

  if (!supabaseUrl || !supabaseKey) {
    throw new Error('Missing Supabase environment variables');
  }

  return createBrowserClient(
    supabaseUrl,
    supabaseKey
  )
}
```

### Usage Examples

<CodeGroup>
  ```typescript Authentication theme={null}
  'use client';
  import { createClient } from '@/utils/supabase/client';

  export default function LoginForm() {
    const supabase = createClient();

    const handleLogin = async (email: string, password: string) => {
      const { data, error } = await supabase.auth.signInWithPassword({
        email,
        password,
      });

      if (error) {
        console.error('Login error:', error.message);
        return;
      }

      console.log('Logged in user:', data.user);
    };

    return (
      <form onSubmit={(e) => {
        e.preventDefault();
        const formData = new FormData(e.currentTarget);
        handleLogin(
          formData.get('email') as string,
          formData.get('password') as string
        );
      }}>
        {/* form fields */}
      </form>
    );
  }
  ```

  ```typescript Database Queries theme={null}
  'use client';
  import { createClient } from '@/utils/supabase/client';

  export default function UserLibrary() {
    const supabase = createClient();

    const fetchLibrary = async () => {
      const { data, error } = await supabase
        .from('user_library')
        .select('*')
        .order('updated_at', { ascending: false });

      if (error) {
        console.error('Error fetching library:', error);
        return [];
      }

      return data;
    };

    // Use in component...
  }
  ```

  ```typescript Real-time Subscriptions theme={null}
  'use client';
  import { createClient } from '@/utils/supabase/client';
  import { useEffect, useState } from 'react';

  export default function LibraryLiveUpdates() {
    const supabase = createClient();
    const [library, setLibrary] = useState([]);

    useEffect(() => {
      // Subscribe to changes
      const channel = supabase
        .channel('library_changes')
        .on(
          'postgres_changes',
          {
            event: '*',
            schema: 'public',
            table: 'user_library'
          },
          (payload) => {
            console.log('Change received!', payload);
            // Update local state
          }
        )
        .subscribe();

      return () => {
        supabase.removeChannel(channel);
      };
    }, []);

    return <div>{/* render library */}</div>;
  }
  ```
</CodeGroup>

## Server-Side Usage

Use the server-side Supabase client for Server Components, Route Handlers, and Server Actions.

### Creating a Server Client

```typescript theme={null}
// src/utils/supabase/server.ts:4
import { createClient } from '@/utils/supabase/server';

const supabase = await createClient();
```

<Note>
  The server client is an async function that must be awaited. It handles cookie management automatically.
</Note>

### Implementation Details

```typescript theme={null}
// src/utils/supabase/server.ts:1
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // Ignore errors in Server Components
            // Middleware will handle session refresh
          }
        },
      },
    }
  )
}
```

### Usage Examples

<CodeGroup>
  ```typescript Server Component theme={null}
  // app/dashboard/page.tsx
  import { createClient } from '@/utils/supabase/server';
  import { redirect } from 'next/navigation';

  export default async function DashboardPage() {
    const supabase = await createClient();

    // Get authenticated user
    const { data: { user } } = await supabase.auth.getUser();

    if (!user) {
      redirect('/login');
    }

    // Fetch user's library
    const { data: library } = await supabase
      .from('user_library')
      .select('*')
      .order('updated_at', { ascending: false });

    return (
      <div>
        <h1>Welcome, {user.email}</h1>
        <LibraryList items={library} />
      </div>
    );
  }
  ```

  ```typescript Server Action theme={null}
  // app/actions/library.ts
  'use server';
  import { createClient } from '@/utils/supabase/server';
  import { revalidatePath } from 'next/cache';

  export async function addToLibrary(animeId: number, title: string) {
    const supabase = await createClient();

    const { data: { user } } = await supabase.auth.getUser();
    
    if (!user) {
      return { error: 'User not authenticated' };
    }

    const { data, error } = await supabase
      .from('user_library')
      .insert({
        user_id: user.id,
        anime_id_jikan: animeId,
        title: title,
        status: 'plan_to_watch'
      })
      .select()
      .single();

    if (error) {
      return { error: error.message };
    }

    revalidatePath('/library');
    return { data };
  }
  ```

  ```typescript Route Handler theme={null}
  // app/api/library/route.ts
  import { createClient } from '@/utils/supabase/server';
  import { NextResponse } from 'next/server';

  export async function GET() {
    const supabase = await createClient();

    const { data: { user } } = await supabase.auth.getUser();

    if (!user) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    }

    const { data, error } = await supabase
      .from('user_library')
      .select('*');

    if (error) {
      return NextResponse.json({ error: error.message }, { status: 500 });
    }

    return NextResponse.json({ data });
  }
  ```
</CodeGroup>

## Middleware Integration

The middleware updates the user session on every request, ensuring authentication state is always fresh.

### Implementation

```typescript theme={null}
// src/utils/supabase/middleware.ts:1
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'

export async function updateSession(request: NextRequest) {
  let supabaseResponse = NextResponse.next({
    request,
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value }) => 
            request.cookies.set(name, value)
          )
          supabaseResponse = NextResponse.next({ request })
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options)
          )
        },
      },
    }
  )

  // Refresh the auth token
  await supabase.auth.getUser()

  return supabaseResponse
}
```

### Using in middleware.ts

```typescript theme={null}
// middleware.ts
import { updateSession } from '@/utils/supabase/middleware'

export async function middleware(request: NextRequest) {
  return await updateSession(request)
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
}
```

<Note>
  The middleware automatically refreshes the user session on every request, keeping authentication state synchronized between client and server.
</Note>

## Authentication Patterns

### Sign Up

<CodeGroup>
  ```typescript Email/Password theme={null}
  const { data, error } = await supabase.auth.signUp({
    email: 'user@example.com',
    password: 'securepassword',
    options: {
      data: {
        full_name: 'John Doe',
        username: 'johndoe',
      }
    }
  })
  ```

  ```typescript OAuth (Google) theme={null}
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'google',
    options: {
      redirectTo: `${window.location.origin}/auth/callback`
    }
  })
  ```
</CodeGroup>

### Sign In

```typescript theme={null}
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'securepassword',
})
```

### Sign Out

```typescript theme={null}
const { error } = await supabase.auth.signOut()
```

### Get Current User

<CodeGroup>
  ```typescript Client-Side theme={null}
  const { data: { user } } = await supabase.auth.getUser()
  ```

  ```typescript Server-Side theme={null}
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()

  if (!user) {
    redirect('/login')
  }
  ```
</CodeGroup>

## Database Operations

### Insert

```typescript theme={null}
const { data, error } = await supabase
  .from('user_library')
  .insert({
    anime_id_jikan: 5114,
    title: 'Fullmetal Alchemist: Brotherhood',
    status: 'watching',
    score: 9
  })
  .select()
  .single()
```

### Select

```typescript theme={null}
// Select all
const { data, error } = await supabase
  .from('user_library')
  .select('*')

// Select with filters
const { data, error } = await supabase
  .from('user_library')
  .select('*')
  .eq('status', 'watching')
  .order('updated_at', { ascending: false })
```

### Update

```typescript theme={null}
const { data, error } = await supabase
  .from('user_library')
  .update({ score: 10, status: 'completed' })
  .eq('anime_id_jikan', 5114)
  .select()
  .single()
```

### Delete

```typescript theme={null}
const { error } = await supabase
  .from('user_library')
  .delete()
  .eq('anime_id_jikan', 5114)
```

## Type Safety

Generate TypeScript types from your database schema:

```bash theme={null}
npx supabase gen types typescript --project-id your-project-ref > src/types/database.types.ts
```

Then use them in your code:

```typescript theme={null}
import type { Database } from '@/types/database.types'

const supabase = createClient<Database>()

// Now all queries are fully typed
const { data } = await supabase
  .from('user_library')
  .select('*')
// data is typed as Database['public']['Tables']['user_library']['Row'][]
```

## Best Practices

<Expandable title="1. Use Appropriate Client">
  * **Client Component**: Use `@/utils/supabase/client`
  * **Server Component**: Use `@/utils/supabase/server`
  * **Middleware**: Use `@/utils/supabase/middleware`
</Expandable>

<Expandable title="2. Handle Authentication State">
  ```typescript theme={null}
  // Always check for user before protected operations
  const { data: { user } } = await supabase.auth.getUser()

  if (!user) {
    // Handle unauthenticated state
    redirect('/login')
  }
  ```
</Expandable>

<Expandable title="3. Error Handling">
  ```typescript theme={null}
  const { data, error } = await supabase
    .from('user_library')
    .select('*')

  if (error) {
    console.error('Database error:', error.message)
    // Handle error appropriately
  }
  ```
</Expandable>

<Expandable title="4. Use RLS Policies">
  Always enable Row Level Security on tables. See [Row Level Security](/integration/row-level-security) for details.
</Expandable>

## Common Issues

<Warning>
  **Error: Missing Supabase environment variables**

  Ensure both `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` are set in your `.env.local` file.
</Warning>

<Warning>
  **Error: Cookie manipulation in Server Components**

  This warning is expected and can be ignored. The middleware handles session refresh automatically.
</Warning>

## Related Resources

<CardGroup cols={2}>
  <Card title="Database Schema" icon="table" href="/integration/database-schema">
    View complete database schema
  </Card>

  <Card title="Row Level Security" icon="shield" href="/integration/row-level-security">
    Learn about RLS policies
  </Card>

  <Card title="Supabase Docs" icon="book" href="https://supabase.com/docs">
    Official Supabase documentation
  </Card>

  <Card title="Library Service" icon="code" href="/development/services">
    High-level library operations
  </Card>
</CardGroup>
