ignitionstack.pro v1.0 is out! Read the announcement →
Skip to Content

Supabase Setup

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.

Quick Checklist

Environment Variables

Create a project

  1. Acesse supabase.com 
  2. Create New Project
  3. Choose a region near your users (e.g., South America (Sao Paulo))
  4. Save the database password (you’ll need it)

Retrieve credentials

In the project dashboard → Settings → API:

.env.local
# 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/postgres

SUPABASE_SERVICE_ROLE_KEY has full DB access. Never expose it client-side!

Install dependencies

npm install @supabase/supabase-js @supabase/ssr

Client configuration

Client-side (Browser)

src/lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr' export function createClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ) }

Server-side (Server Components/Actions)

src/lib/supabase/server.ts
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 } }, }, } ) }

Admin client (bypass RLS)

src/lib/supabase/admin.ts
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, }, } ) }

Middleware (auth)

src/middleware.ts
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

Migrations live under supabase/migrations/. Run them with:

# Apply migrations npx supabase db push # Or use the Supabase CLI supabase db push

Table structure

supabase/migrations/001_initial.sql
-- 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;

Storage Buckets

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');

Verify the connection

src/app/api/health/route.ts
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 }) }

Next steps

Troubleshooting

Connection error

RLS blocking queries

Cookies do not persist

Resources