Supabase is the official backend for ignitionstack.pro. It provides auth, a relational database, storage, and webhooks that power our Server Actions and admin panel. This guide consolidates the initial setup.
For detailed documentation of each Supabase feature, see the Supabase section.
South America (Sao Paulo))In the project dashboard → Settings → API:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxxxxxxxxxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Database (Settings → Database → Connection string)
DATABASE_URL=postgresql://postgres:[PASSWORD]@db.xxxxxxxxxxxxx.supabase.co:5432/postgresSUPABASE_SERVICE_ROLE_KEY has full DB access. Never expose it client-side!
npm install @supabase/supabase-js @supabase/ssrimport { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}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 {
// Server Component - ignore
}
},
},
}
)
}import { createClient } from '@supabase/supabase-js'
export function createAdminClient() {
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
auth: {
autoRefreshToken: false,
persistSession: false,
},
}
)
}import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
export async function middleware(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)
)
},
},
}
)
const { data: { user } } = await supabase.auth.getUser()
// Protect admin routes
if (request.nextUrl.pathname.startsWith('/admin') && !user) {
return NextResponse.redirect(new URL('/login', request.url))
}
return supabaseResponse
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}Migrations live under supabase/migrations/. Run them with:
# Apply migrations
npx supabase db push
# Or use the Supabase CLI
supabase db push-- Users (extends auth.users)
create table public.profiles (
id uuid references auth.users on delete cascade primary key,
email text,
full_name text,
avatar_url text,
stripe_customer_id text,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- Products
create table public.products (
id uuid default gen_random_uuid() primary key,
slug text unique not null,
name text not null,
description text,
price decimal(10,2),
is_active boolean default true,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- Enable RLS
alter table public.profiles enable row level security;
alter table public.products enable row level security;Configure via Dashboard → Storage or SQL:
-- Create bucket for public images
insert into storage.buckets (id, name, public)
values ('images', 'images', true);
-- Policy for authenticated uploads
create policy "Users can upload images"
on storage.objects for insert
to authenticated
with check (bucket_id = 'images');
-- Policy for public reads
create policy "Public can view images"
on storage.objects for select
to public
using (bucket_id = 'images');import { createClient } from '@/lib/supabase/server'
export async function GET() {
const supabase = await createClient()
const { data, error } = await supabase.from('profiles').select('count')
if (error) {
return Response.json({ status: 'error', message: error.message }, { status: 500 })
}
return Response.json({ status: 'ok', data })
}@supabase/ssr correctly