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
Get Supabase URL
Go to app.supabase.com
Select your project
Navigate to Settings > API
Copy the “Project URL”
Get Anon Key
In the same API settings page
Copy the “anon public” key
This key is safe to use in the browser
Create .env.local
Create the file in your project root and paste both values
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:
# 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:
Why configure remote patterns?
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:
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:
src/utils/supabase/client.ts
src/utils/supabase/server.ts
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:
Profiles RLS
User Library RLS
-- 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:
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:
{
"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:
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:
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:
NEXT_PUBLIC_SUPABASE_URL = your_production_url
NEXT_PUBLIC_SUPABASE_ANON_KEY = your_production_key
NODE_ENV = production
Build Optimization
Optimize your production build:
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:
Connect Repository
Import your Git repository in Vercel dashboard
Configure Environment Variables
Add your Supabase credentials in the Vercel project settings
Deploy
Vercel automatically detects Next.js and deploys
Vercel automatically sets NODE_ENV=production and enables optimizations.
Troubleshooting
Environment variables not loading
Solution:
Ensure .env.local is in the project root
Restart the development server
Check for typos in variable names
Verify the NEXT_PUBLIC_ prefix for client-side variables
Solution:
Verify next.config.ts includes the CDN hostname
Check that the hostname matches exactly (no trailing slash)
Restart the dev server after config changes
Database connection issues
Solution:
Verify Supabase project is active
Check API keys are correct
Ensure RLS policies are properly configured
Check Supabase service status
Solution:
Run npm install to ensure all types are installed
Delete .next folder and rebuild
Check tsconfig.json paths configuration