npm install @supabase/supabase-js | Install client |
npx supabase init | Initialize project |
npx supabase start | Start local dev |
npx supabase stop | Stop local dev |
npx supabase db reset | Reset database |
npx supabase db diff -f migration_name | Generate migration |
npx supabase db push | Push migrations |
npx supabase gen types typescript | Generate types |
npx supabase link --project-ref PROJECT_ID | Link project |
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
'https://xxx.supabase.co',
'public-anon-key'
);
// With custom options
const supabase = createClient(url, key, {
auth: {
autoRefreshToken: true,
persistSession: true,
},
global: {
headers: { 'x-custom-header': 'value' },
},
}); import { createClient } from '@supabase/supabase-js';
import { Database } from './types/supabase';
const supabase = createClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
); // Select all
const { data, error } = await supabase
.from('users')
.select('*');
// Select specific columns
const { data } = await supabase
.from('users')
.select('id, name, email');
// Select with relations
const { data } = await supabase
.from('posts')
.select(`
id,
title,
author:users(name, email),
comments(id, text)
`);
// Single row
const { data } = await supabase
.from('users')
.select('*')
.single(); // Insert single
const { data, error } = await supabase
.from('users')
.insert({ name: 'John', email: 'john@example.com' })
.select();
// Insert multiple
const { data } = await supabase
.from('users')
.insert([
{ name: 'John', email: 'john@example.com' },
{ name: 'Jane', email: 'jane@example.com' },
])
.select();
// Upsert
const { data } = await supabase
.from('users')
.upsert({ id: 1, name: 'Updated Name' })
.select(); const { data, error } = await supabase
.from('users')
.update({ name: 'New Name' })
.eq('id', 1)
.select(); const { error } = await supabase
.from('users')
.delete()
.eq('id', 1); .eq('column', 'value') // Equal
.neq('column', 'value') // Not equal
.gt('column', value) // Greater than
.gte('column', value) // Greater than or equal
.lt('column', value) // Less than
.lte('column', value) // Less than or equal .like('column', '%pattern%') // LIKE
.ilike('column', '%pattern%') // ILIKE (case insensitive)
.textSearch('column', 'query') // Full-text search .contains('tags', ['react']) // Array contains
.containedBy('tags', ['a', 'b']) // Array contained by
.overlaps('tags', ['a', 'b']) // Arrays overlap
// JSON
.eq('metadata->key', 'value') // JSON field .or('age.gt.20,age.lt.10') // OR
.not('status', 'eq', 'active') // NOT
.in('status', ['active', 'pending']) // IN
.is('deleted_at', null) // IS NULL .order('created_at', { ascending: false })
.limit(10)
.range(0, 9) // Pagination (0-9) // Sign up
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'password123',
options: {
data: { name: 'John' }, // User metadata
},
});
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'password123',
});
// Sign out
await supabase.auth.signOut(); // Sign in with OAuth
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google', // github, discord, etc.
options: {
redirectTo: 'https://example.com/callback',
},
});
// Handle callback
const { data, error } = await supabase.auth.exchangeCodeForSession(code); const { data, error } = await supabase.auth.signInWithOtp({
email: 'user@example.com',
options: {
emailRedirectTo: 'https://example.com/welcome',
},
}); // Get current user
const { data: { user } } = await supabase.auth.getUser();
// Get session
const { data: { session } } = await supabase.auth.getSession();
// Listen to auth changes
supabase.auth.onAuthStateChange((event, session) => {
console.log(event, session);
// SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED, etc.
}); // Send reset email
const { data, error } = await supabase.auth.resetPasswordForEmail(
'user@example.com',
{ redirectTo: 'https://example.com/reset' }
);
// Update password
const { data, error } = await supabase.auth.updateUser({
password: 'new-password',
}); // Subscribe to changes
const channel = supabase
.channel('schema-db-changes')
.on(
'postgres_changes',
{
event: '*', // INSERT, UPDATE, DELETE, *
schema: 'public',
table: 'messages',
filter: 'room_id=eq.123',
},
(payload) => {
console.log('Change:', payload);
}
)
.subscribe();
// Unsubscribe
await supabase.removeChannel(channel); // Send message
const channel = supabase.channel('room-1');
channel.subscribe((status) => {
if (status === 'SUBSCRIBED') {
channel.send({
type: 'broadcast',
event: 'cursor-pos',
payload: { x: 100, y: 200 },
});
}
});
// Receive message
channel.on('broadcast', { event: 'cursor-pos' }, (payload) => {
console.log(payload);
}); const channel = supabase.channel('room-1');
channel
.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState();
console.log('Online users:', state);
})
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
console.log('Joined:', newPresences);
})
.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
console.log('Left:', leftPresences);
})
.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
await channel.track({ user_id: '123', online_at: new Date() });
}
}); // Upload file
const { data, error } = await supabase.storage
.from('avatars')
.upload('public/avatar.png', file, {
cacheControl: '3600',
upsert: false,
});
// Upload from base64
const { data, error } = await supabase.storage
.from('avatars')
.upload('public/avatar.png', decode(base64), {
contentType: 'image/png',
}); // Get public URL
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('public/avatar.png');
// Download file
const { data, error } = await supabase.storage
.from('avatars')
.download('public/avatar.png');
// Create signed URL
const { data, error } = await supabase.storage
.from('private')
.createSignedUrl('file.pdf', 3600); // 1 hour // List files
const { data, error } = await supabase.storage
.from('avatars')
.list('public', {
limit: 100,
offset: 0,
sortBy: { column: 'name', order: 'asc' },
});
// Delete file
const { error } = await supabase.storage
.from('avatars')
.remove(['public/avatar.png']);
// Move file
const { data, error } = await supabase.storage
.from('avatars')
.move('old/path.png', 'new/path.png'); // supabase/functions/hello/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
serve(async (req) => {
const { name } = await req.json();
return new Response(
JSON.stringify({ message: `Hello ${name}!` }),
{ headers: { "Content-Type": "application/json" } }
);
}); const { data, error } = await supabase.functions.invoke('hello', {
body: { name: 'World' },
});
console.log(data); // { message: "Hello World!" } # Create new function
npx supabase functions new hello
# Serve locally
npx supabase functions serve hello
# Deploy
npx supabase functions deploy hello
# Delete
npx supabase functions delete hello -- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Create policy for select
CREATE POLICY "Users can view their own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
-- Create policy for insert
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Create policy for update
CREATE POLICY "Users can update their own posts"
ON posts FOR UPDATE
USING (auth.uid() = user_id);
-- Create policy for delete
CREATE POLICY "Users can delete their own posts"
ON posts FOR DELETE
USING (auth.uid() = user_id); -- Public read access
CREATE POLICY "Public read access"
ON posts FOR SELECT
TO anon, authenticated
USING (true);
-- Authenticated users only
CREATE POLICY "Authenticated only"
ON posts FOR ALL
TO authenticated
USING (true);
-- Team-based access
CREATE POLICY "Team members can view"
ON posts FOR SELECT
USING (
team_id IN (
SELECT team_id FROM team_members
WHERE user_id = auth.uid()
)
);