正在加载,请稍候…

Supabase: PostgreSQL with Superpowers — Auth, Realtime, and Edge Functions

Build full-stack apps with Supabase — Row Level Security, real-time subscriptions, auth providers, storage, edge functions, and the JavaScript client library.

Supabase Architecture

Supabase wraps PostgreSQL with real-time subscriptions, REST/GraphQL APIs, auth, storage, and edge functions — all open-source.

Setup

npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js'

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

Row Level Security (RLS)

-- Enable RLS
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 create posts
CREATE POLICY "Users create posts" ON posts
  FOR INSERT WITH CHECK (auth.uid() = user_id);

-- Policy: users can update own posts
CREATE POLICY "Users update own posts" ON posts
  FOR UPDATE USING (auth.uid() = user_id);

-- Policy: public posts are visible to everyone
CREATE POLICY "Public posts visible" ON posts
  FOR SELECT USING (published = true);

Authentication

// Email/password
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password',
  options: {
    data: { display_name: 'Alice' },
  },
})

// Social OAuth
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: { redirectTo: `${window.location.origin}/auth/callback` },
})

// Get current user
const { data: { user } } = await supabase.auth.getUser()

// Auth state listener
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_IN') {
    setUser(session?.user ?? null)
  } else if (event === 'SIGNED_OUT') {
    setUser(null)
  }
})

Database Queries

// Type-safe with generated types
type Database = {
  public: {
    Tables: {
      posts: {
        Row: { id: string; title: string; content: string; user_id: string }
        Insert: { title: string; content: string }
        Update: Partial<{ title: string; content: string }>
      }
    }
  }
}

const supabase = createClient<Database>(URL, KEY)

// CRUD operations
const { data: posts, error } = await supabase
  .from('posts')
  .select(`
    id,
    title,
    content,
    created_at,
    user:users(id, name, avatar_url)
  `)
  .eq('published', true)
  .order('created_at', { ascending: false })
  .range(0, 19)

// Insert
const { data: post } = await supabase
  .from('posts')
  .insert({ title: 'Hello', content: 'World' })
  .select()
  .single()

// Update
const { data } = await supabase
  .from('posts')
  .update({ title: 'Updated' })
  .eq('id', postId)
  .select()
  .single()

Real-time Subscriptions

// Subscribe to all changes on a table
const channel = supabase
  .channel('posts-changes')
  .on(
    'postgres_changes',
    { event: '*', schema: 'public', table: 'posts' },
    (payload) => {
      console.log('Change:', payload.eventType, payload.new)
    }
  )
  .subscribe()

// Subscribe to specific rows
const channel = supabase
  .channel('my-post')
  .on(
    'postgres_changes',
    {
      event: 'UPDATE',
      schema: 'public',
      table: 'posts',
      filter: `id=eq.${postId}`,
    },
    (payload) => setPost(payload.new)
  )
  .subscribe()

// Cleanup
return () => supabase.removeChannel(channel)

Storage

// Upload file
const { data, error } = await supabase.storage
  .from('avatars')
  .upload(`${userId}/avatar.jpg`, file, {
    cacheControl: '3600',
    upsert: true,
    contentType: 'image/jpeg',
  })

// Get public URL
const { data: { publicUrl } } = supabase.storage
  .from('avatars')
  .getPublicUrl(`${userId}/avatar.jpg`)

// Signed URL (for private buckets)
const { data: { signedUrl } } = await supabase.storage
  .from('private-docs')
  .createSignedUrl('document.pdf', 3600) // 1 hour expiry

Edge Functions

// supabase/functions/send-welcome/index.ts
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'

serve(async (req) => {
  const { email, name } = await req.json()
  
  await sendEmail({ to: email, subject: 'Welcome!', body: `Hello ${name}` })
  
  return new Response(JSON.stringify({ sent: true }), {
    headers: { 'Content-Type': 'application/json' },
  })
})
supabase functions deploy send-welcome
supabase functions invoke send-welcome --body '{"email":"user@example.com","name":"Alice"}'