Create tables, define Row Level Security, and query data with the Supabase client.
Create tables in the Supabase dashboard Table Editor (GUI) or write SQL in the SQL Editor. Both work — SQL is more powerful for complex schemas.
-- In Supabase SQL Editor
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
title TEXT NOT NULL,
body TEXT,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Policy: users can only see their own posts
CREATE POLICY 'Users see own posts' ON posts
FOR SELECT USING (auth.uid() = user_id);
-- Policy: users can insert their own posts
CREATE POLICY 'Users insert own posts' ON posts
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- Policy: public posts visible to all
CREATE POLICY 'Public posts readable' ON posts
FOR SELECT USING (true); -- or add a published column check// SELECT
const { data, error } = await supabase
.from('posts')
.select('id, title, created_at')
.eq('user_id', userId)
.order('created_at', { ascending: false })
.limit(10);
// SELECT with JOIN (foreign key)
const { data } = await supabase
.from('posts')
.select('*, profiles(name, avatar_url)');
// INSERT
const { data } = await supabase.from('posts').insert({
title: 'My Post',
body: 'Content here',
user_id: user.id
}).select();
// UPDATE
await supabase.from('posts')
.update({ title: 'New Title' })
.eq('id', postId);
// DELETE
await supabase.from('posts').delete().eq('id', postId);auth.uid() in RLS policies returns the logged-in user's ID. The database checks it automatically..from(table).select().eq().order().limit() chain.