How I built the content management backend for this portfolio — covering Server Actions, Row Level Security, and the admin dashboard architecture.
When I decided to rebuild my portfolio, I had one core requirement: I wanted to manage all my content — blog posts, projects, work experience — from a clean admin interface, without paying for a third-party CMS.
The solution I landed on uses:
The key insight is that Supabase's Row Level Security does the heavy lifting. Public visitors can only read published posts. The admin account can read and write everything.
create policy "Public can read published posts"
on public.posts for select to anon
using (published = true);
Server Actions make the CRUD layer almost trivial. A create action looks like:
async function createPost(data: PostInput) {
"use server";
const supabase = createClient();
const { error } = await supabase.from("posts").insert(data);
if (error) throw new Error(error.message);
revalidatePath("/blog");
}
The revalidatePath call invalidates the Next.js cache so the public blog page reflects the change immediately.
The biggest gotcha was session management across Server Components and the Edge Runtime (middleware). The @supabase/ssr package handles this correctly by reading and writing cookies on both the request and response objects.