| [ Web Proxy ] |
| Viewing: https://marrowstack.dev/docs/blocks/admin | [Back] [Original] |
Integration guide
Production admin backend: user management, revenue analytics, feature flags with rollout percentages, audit log, and CSV export. One file, Supabase-native.
Read the Getting Access guideif you haven't yet.
Sign in at marrowstack.dev, open the Admin Dashboard block, and click Copy all files. Paste lib/admin.ts (~510 lines) into your project. The SQL migration is embedded in the MIGRATION constant.
This block is server-side only. It exports typed async functions (no React components). Call them from your API routes or Server Actions and build your own UI on top.
admin.ts from the block detail page and paste it into lib/admin.ts in your project.npm install @supabase/supabase-js zod| Variable | Required | Default | Purpose |
|---|---|---|---|
| NEXT_PUBLIC_SUPABASE_URL | yes | Your Supabase project URL | |
| SUPABASE_SERVICE_ROLE_KEY | yes | Service role key server-side only |
Run the MIGRATION constant from admin.ts in Supabase SQL Editor. Creates: feature_flags, admin_audit_log, and views over the profiles/orders tables. Requires the profiles table from the Auth block.
import { listUsers, banUser } from '@/lib/admin'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { NextRequest, NextResponse } from 'next/server'
export async function GET() {
const session = await getServerSession(authOptions)
if (session?.user?.role !== 'admin') return new NextResponse('Forbidden', { status: 403 })
const users = await listUsers({ page: 1, pageSize: 50 })
return NextResponse.json(users)
}
export async function POST(req: NextRequest) {
const session = await getServerSession(authOptions)
if (session?.user?.role !== 'admin') return new NextResponse('Forbidden', { status: 403 })
const { userId } = await req.json()
await banUser(userId, session.user.id)
return NextResponse.json({ ok: true })
}listUsers({ page: 1, pageSize: 10 }) from a test route. Confirm it returns your user rows from Supabase.createFeatureFlag({ key: 'test', enabled: true, rolloutPct: 100 }) and confirm the row appears in feature_flags.getRevenueSummary() and confirm it returns aggregated data.exportUsersCsv() and confirm the returned string is valid CSV.relation 'feature_flags' does not exist
Cause: The migration has not been run.
Fix: Run the MIGRATION constant SQL in Supabase SQL Editor.
row-level security policy violation
Cause: Using the anon key instead of the service role key.
Fix: Set SUPABASE_SERVICE_ROLE_KEY. The admin client must use this key.
column 'role' does not exist on profiles
Cause: The Auth block's profiles table is missing the role column, or the Auth migration has not been run.
Fix: Run the Auth block's MIGRATION first. Admin depends on it.
Cannot read properties of undefined (reading 'rows')
Cause: Supabase query returned null table does not exist or RLS blocked it.
Fix: Check that the migration ran and that the service role key is correct.
What this block handles:
admin_audit_log with actor ID and timestamp.What you must ensure:
| Web Proxy Viewer | New URL | Original Page |