Supabase Essentials
Supabase Essentials Client Setup & Auth import { createClient } from '@supabase/supabase-js'; import type { Database } from './database.types'; // generated typ…
Supabase Essentials
Client Setup & Auth
import { createClient } from '@supabase/supabase-js';
import type { Database } from './database.types'; // generated types
const supabase = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
// Auth
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'password123',
options: { data: { full_name: 'Alice' } },
});
const { error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'password123',
});
await supabase.auth.signInWithOAuth({ provider: 'github' });
await supabase.auth.signOut();
// Get current user
const { data: { user } } = await supabase.auth.getUser();
const { data: { session } } = await supabase.auth.getSession();
// Listen to auth state changes
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN') console.log('User signed in', session?.user);
if (event === 'SIGNED_OUT') console.log('User signed out');
});Database Queries
// SELECT
const { data, error } = await supabase
.from('posts')
.select('id, title, body, created_at, author:users(name, email)')
.eq('status', 'published')
.order('created_at', { ascending: false })
.range(0, 19); // pagination: rows 0-19
// Filters
.eq('id', userId)
.neq('status', 'deleted')
.gt('views', 100)
.gte('age', 18)
.in('role', ['admin', 'moderator'])
.like('name', '%alice%')
.ilike('name', '%alice%') // case-insensitive
.is('deleted_at', null)
.not('status', 'eq', 'hidden')
.or('role.eq.admin,role.eq.moderator')
// INSERT
const { data, error } = await supabase
.from('posts')
.insert({ title: 'Hello', body: 'World', author_id: user.id })
.select()
.single();
// UPSERT
await supabase
.from('profiles')
.upsert({ id: user.id, username: 'alice' }, { onConflict: 'id' });
// UPDATE
await supabase
.from('posts')
.update({ status: 'published' })
.eq('id', postId)
.eq('author_id', userId); // RLS-style safety
// DELETE
await supabase
.from('posts')
.delete()
.eq('id', postId);
// Count
const { count } = await supabase
.from('posts')
.select('*', { count: 'exact', head: true })
.eq('status', 'published');Row Level Security & Realtime
-- Row Level Security (RLS) — SQL policies
-- Enable RLS on table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Policy: users can only read their own posts
CREATE POLICY "users read own posts"
ON posts FOR SELECT
USING (auth.uid() = author_id);
-- Policy: users can insert posts as themselves
CREATE POLICY "users insert own posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = author_id);
-- Policy: everyone can read published posts
CREATE POLICY "public read published"
ON posts FOR SELECT
USING (status = 'published');
-- Policy: admins can do anything
CREATE POLICY "admins full access"
ON posts
USING (auth.jwt() ->> 'role' = 'admin');// Realtime subscriptions
const channel = supabase
.channel('posts-changes')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'posts', filter: `author_id=eq.${userId}` },
(payload) => {
if (payload.eventType === 'INSERT') addPost(payload.new);
if (payload.eventType === 'UPDATE') updatePost(payload.new);
if (payload.eventType === 'DELETE') removePost(payload.old.id);
}
)
.subscribe();
// Cleanup
supabase.removeChannel(channel);
// Storage
const { data, error } = await supabase.storage
.from('avatars')
.upload(`${userId}/avatar.png`, file, { upsert: true });
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}/avatar.png`);