Supabase database in Next.js — from zero to CRUD
Set up Supabase as your database in Next.js. Create a table, insert data, read rows, update, and delete — with full code for each step. No SQL experience needed.
Why Supabase for your database?
Supabase gives you a PostgreSQL database with a simple JavaScript API. No SQL needed for basic operations — you use supabase.from("table").select() and it just works. It also comes with real-time subscriptions and Row Level Security so your data is safe.
Step 1: Create a table
In the Supabase Dashboard, go to Table Editor → New Table. Let's create a simple "todos" table:
-- You can run this in Supabase SQL Editor
-- or just use the Table Editor UI
create table todos (
id uuid default gen_random_uuid() primary key,
user_id uuid references auth.users(id),
title text not null,
done boolean default false,
created_at timestamptz default now()
);
-- Enable Row Level Security
alter table todos enable row level security;
-- Users can only see their own todos
create policy "Users see own todos"
on todos for select
using (auth.uid() = user_id);
-- Users can only insert their own todos
create policy "Users insert own todos"
on todos for insert
with check (auth.uid() = user_id);Step 2: Read data (SELECT)
// app/dashboard/page.tsx — Server Component
import { createClient } from "@/libs/supabase/server";
export default async function Dashboard() {
const supabase = await createClient();
const { data: todos, error } = await supabase
.from("todos")
.select("*")
.order("created_at", { ascending: false });
if (error) {
return <p>Error loading todos</p>;
}
return (
<ul>
{todos?.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}Step 3: Insert data (INSERT)
// app/api/todos/route.ts
import { createClient } from "@/libs/supabase/server";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ error: "Not logged in" }, { status: 401 });
}
const { title } = await req.json();
const { data, error } = await supabase
.from("todos")
.insert({ title, user_id: user.id })
.select()
.single();
if (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
return NextResponse.json(data);
}Step 4: Update data (UPDATE)
// Mark a todo as done
const { error } = await supabase
.from("todos")
.update({ done: true })
.eq("id", todoId);Step 5: Delete data (DELETE)
// Delete a todo
const { error } = await supabase
.from("todos")
.delete()
.eq("id", todoId);That's CRUD
Create (insert), Read (select), Update, Delete — the four operations every app needs. Supabase makes each one a single line. Add authentication and Row Level Security, and your data is safe without writing any extra backend code.
Database already configured
Delfy comes with Supabase set up — profiles table, RLS policies, server/client helpers, and examples for every CRUD operation.
Get the boilerplate