Skip to main content

Environment Variables

EpiNeko requires specific environment variables to function properly. All configuration is stored in .env.local at the root of your project.
Never commit your .env.local file to version control. It should already be included in .gitignore.

Required Variables

# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
1

Get Supabase URL

  1. Go to app.supabase.com
  2. Select your project
  3. Navigate to Settings > API
  4. Copy the “Project URL”
2

Get Anon Key

  1. In the same API settings page
  2. Copy the “anon public” key
  3. This key is safe to use in the browser
3

Create .env.local

Create the file in your project root and paste both values
4

Restart Development Server

Changes to environment variables require a server restart
The NEXT_PUBLIC_ prefix makes these variables available to the browser. This is required for client-side Supabase operations.

Optional Variables

Add these for enhanced functionality:
.env.local
# Analytics (optional)
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# Jikan API Configuration (optional - uses defaults)
NEXT_PUBLIC_JIKAN_API_URL=https://api.jikan.moe/v4

# Development
NODE_ENV=development

Next.js Configuration

The next.config.ts file controls Next.js behavior and optimization settings.

Current Configuration

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.myanimelist.net',
        port: '',
        pathname: '/**',
      },
    ],
  },
};

export default nextConfig;

Image Optimization

EpiNeko loads anime images from MyAnimeList CDN. The configuration allows Next.js Image component to optimize these external images:
Next.js requires explicit configuration for external image domains to prevent abuse and ensure security. Without this configuration, images from MyAnimeList won’t load.

Adding Additional Image Sources

If you want to allow images from other sources:
next.config.ts
const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'cdn.myanimelist.net',
        port: '',
        pathname: '/**',
      },
      {
        protocol: 'https',
        hostname: 'your-custom-cdn.com',
        port: '',
        pathname: '/images/**',
      },
    ],
  },
};

Other Useful Next.js Options

Enable strict TypeScript checking:
const nextConfig: NextConfig = {
  typescript: {
    ignoreBuildErrors: false, // Fail build on TS errors
  },
}
Enable React strict mode for development:
const nextConfig: NextConfig = {
  reactStrictMode: true,
}
Configure URL redirects:
const nextConfig: NextConfig = {
  async redirects() {
    return [
      {
        source: '/home',
        destination: '/',
        permanent: true,
      },
    ]
  },
}
Make server-side env vars available:
const nextConfig: NextConfig = {
  env: {
    CUSTOM_KEY: process.env.CUSTOM_KEY,
  },
}

Supabase Configuration

Client Configuration

The Supabase clients are configured in the utility files with error handling:
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: NEXT_PUBLIC_SUPABASE_URL or NEXT_PUBLIC_SUPABASE_ANON_KEY');
  }

  return createBrowserClient(
    supabaseUrl,
    supabaseKey
  )
}

Advanced Supabase Options

Customize the Supabase client with additional options:
return createBrowserClient(
  supabaseUrl,
  supabaseKey,
  {
    auth: {
      persistSession: true,
      autoRefreshToken: true,
      detectSessionInUrl: true,
    },
    db: {
      schema: 'public',
    },
    global: {
      headers: {
        'X-Custom-Header': 'value',
      },
    },
  }
)

Database Configuration

Row Level Security (RLS)

EpiNeko uses RLS to protect user data. The policies are configured in the migration file:
-- Enable RLS
alter table public.profiles enable row level security;

-- Anyone can view profiles
create policy "Public profiles are viewable by everyone." 
  on public.profiles
  for select 
  using (true);

-- Users can only insert their own profile
create policy "Users can insert their own profile." 
  on public.profiles
  for insert 
  with check (auth.uid() = id);

-- Users can only update their own profile
create policy "Users can update own profile." 
  on public.profiles
  for update 
  using (auth.uid() = id);
RLS policies are enforced at the database level, providing an additional security layer beyond application logic.

Database Indexes

For better performance, consider adding indexes:
-- Index on username for faster lookups
create index profiles_username_idx on public.profiles(username);

-- Index on user_id for library queries
create index user_library_user_id_idx on public.user_library(user_id);

-- Index on anime_id for duplicate checks
create index user_library_anime_id_idx on public.user_library(anime_id_jikan);

-- Composite index for status filtering
create index user_library_user_status_idx on public.user_library(user_id, status);

Styling Configuration

Tailwind CSS

EpiNeko uses Tailwind CSS v4 with custom configuration. The configuration is minimal by design:
tailwind.config.ts
import type { Config } from 'tailwindcss'

const config: Config = {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

export default config

DaisyUI Configuration

DaisyUI provides pre-built components. Configure themes in tailwind.config.ts:
import daisyui from 'daisyui'

const config: Config = {
  // ... other config
  plugins: [daisyui],
  daisyui: {
    themes: [
      'light',
      'dark',
      'cupcake',
      {
        epineko: {
          'primary': '#570df8',
          'secondary': '#f000b8',
          'accent': '#37cdbe',
          'neutral': '#3d4451',
          'base-100': '#ffffff',
        },
      },
    ],
    darkTheme: 'dark',
    base: true,
    styled: true,
    utils: true,
  },
}

TypeScript Configuration

EpiNeko uses TypeScript with strict mode enabled:
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
The @/* path alias allows you to import from src/ using @/ instead of relative paths.

Middleware Configuration

The middleware configuration controls which routes are protected:
src/middleware.ts
export const config = {
  matcher: [
    /*
     * Match all request paths except:
     * - _next/static (static files)
     * - _next/image (image optimization)
     * - favicon.ico
     * - image files (svg, png, jpg, etc.)
     */
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
}

Customizing Protected Routes

To add route-specific logic:
src/middleware.ts
import { type NextRequest } from 'next/server'
import { updateSession } from '@/utils/supabase/middleware'

export async function middleware(request: NextRequest) {
  const response = await updateSession(request)
  
  // Add custom logic for specific routes
  if (request.nextUrl.pathname.startsWith('/admin')) {
    const supabase = createServerClient(/* ... */)
    const { data: { user } } = await supabase.auth.getUser()
    
    if (!user?.user_metadata?.is_admin) {
      return NextResponse.redirect(new URL('/', request.url))
    }
  }
  
  return response
}

Production Configuration

Environment Variables for Production

When deploying, ensure these variables are set:
Production .env
NEXT_PUBLIC_SUPABASE_URL=your_production_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_production_key
NODE_ENV=production

Build Optimization

Optimize your production build:
next.config.ts
const nextConfig: NextConfig = {
  // Minify output
  swcMinify: true,
  
  // Enable compression
  compress: true,
  
  // Optimize images
  images: {
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 60,
  },
  
  // Configure headers
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'X-DNS-Prefetch-Control',
            value: 'on'
          },
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=63072000; includeSubDomains'
          },
        ],
      },
    ]
  },
}

Vercel Deployment

EpiNeko is optimized for Vercel deployment:
1

Connect Repository

Import your Git repository in Vercel dashboard
2

Configure Environment Variables

Add your Supabase credentials in the Vercel project settings
3

Deploy

Vercel automatically detects Next.js and deploys
Vercel automatically sets NODE_ENV=production and enables optimizations.

Troubleshooting

Solution:
  1. Ensure .env.local is in the project root
  2. Restart the development server
  3. Check for typos in variable names
  4. Verify the NEXT_PUBLIC_ prefix for client-side variables
Solution:
  1. Verify next.config.ts includes the CDN hostname
  2. Check that the hostname matches exactly (no trailing slash)
  3. Restart the dev server after config changes
Solution:
  1. Verify Supabase project is active
  2. Check API keys are correct
  3. Ensure RLS policies are properly configured
  4. Check Supabase service status
Solution:
  1. Run npm install to ensure all types are installed
  2. Delete .next folder and rebuild
  3. Check tsconfig.json paths configuration