编程 Supabase 2026 深度解析:开源 Firebase 的极限进化

2026-05-13 17:51:41 +0800 CST views 5

Supabase 2026 深度解析:开源 Firebase 的极限进化——从数据库到 AI 应用全家桶

前言

2026年,Supabase 已经不再只是"Firebase 的开源替代品"了。

它进化为一套完整的后端即服务(Backend as a Service)平台,构建在 PostgreSQL 之上,提供实时数据库、身份认证、文件存储、边缘函数、向量检索等全套能力。更重要的是,Supabase 将 PostgreSQL 的强大 SQL 能力与 AI 时代的需求深度结合——pgvector 向量存储、RAG 管道支持、实时数据订阅,让开发者无需搭建复杂的后端基础设施,就能快速构建现代 AI 应用。

本文将从 Supabase 的核心架构出发,深入解析 2026 年的关键新特性:模板系统、熔断器模式、向量存储、RAG 集成、以及构建 AI 应用后端的实战指南。

一、Supabase 是什么?不止是 Firebase 替代品

1.1 架构全景

# Supabase 全家桶
# 
# Firebase: 专有云、NoSQL (Firestore)、闭源生态
# Supabase: 开源可自托管、PostgreSQL、SQL 原生、开放生态

Supabase 核心组件:
├── PostgreSQL 17         # 关系型数据库 (核心)
├── pgvector              # 向量存储与检索 (AI 支持)
├── GoTrue               # 身份认证 (JWT)
├── PostgREST            # REST API 自动生成
├── PostgRPC             # gRPC API (高性能)
├── Realtime             # WebSocket 实时订阅
├── Storage              # 文件存储 (S3 兼容)
├── Edge Functions        # 边缘函数 (Deno)
├── Edge Analytics        # 边缘分析
└── Studio               # 管理界面

1.2 为什么选择 PostgreSQL?

-- PostgreSQL 的优势:既可以做传统关系型数据库
-- 也可以做向量数据库、时间序列数据库、全文搜索...

-- 1. 关系型数据
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT UNIQUE NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- 2. 向量数据 (pgvector)
CREATE TABLE document_embeddings (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  content TEXT NOT NULL,
  embedding vector(1536),  -- OpenAI embeddings 维度
  metadata JSONB,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- 3. 时序数据
CREATE TABLE sensor_readings (
  device_id UUID REFERENCES devices(id),
  recorded_at TIMESTAMPTZ DEFAULT now(),
  temperature FLOAT,
  humidity FLOAT
) PARTITION BY RANGE (recorded_at);

-- 4. 全文搜索
CREATE TABLE articles (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  title TEXT,
  body TEXT,
  search_vector TSVECTOR  -- 全文搜索向量
);

-- 5. 行级安全 (RLS) - Supabase 认证的核心
CREATE POLICY "Users can view own data"
  ON users
  FOR SELECT
  USING (auth.uid() = id);  -- 自动化与 GoTrue 认证集成

二、模板系统:开发效率提升 300%

2.1 模板系统设计

// Supabase 模板系统
// 核心思想:将常见应用模式封装为可复用的模板

// 模板类型
type Template =
  | "saas-starter"        // SaaS 应用基础架构
  | "social-app"           // 社交应用
  | "e-commerce"           // 电商
  | "ai-chatbot"           // AI 聊天机器人
  | "real-time-collab"    // 实时协作
  | "rag-pipeline"        // RAG 管道

// 模板包含
interface Template {
  id: string
  name: string
  description: string
  
  // 数据库 Schema
  schemas: {
    tables: Table[]
    functions: Function[]
    triggers: Trigger[]
    policies: Policy[]  // RLS 策略
  }[]
  
  // API 配置
  apis: {
    rest: Route[]      // PostgREST 自动生成
    rpc: Procedure[]   // RPC 函数
  }[]
  
  // 前端示例
  frontend?: {
    framework: "react" | "vue" | "svelte"
    code: string
  }
  
  // 边缘函数
  edgeFunctions: EdgeFunction[]
}

// 使用模板创建项目
const { data, error } = await supabase.cli.projects.create({
  template: "saas-starter",
  name: "my-saas-app",
  organization: "my-org",
})

console.log("Project created with:")
console.log("- 8 tables (users, teams, subscriptions, ...)")
console.log("- 15 RLS policies")
console.log("- 5 RPC functions")
console.log("- React starter code")

2.2 SaaS 模板实战

-- Supabase SaaS Starter 模板的核心表结构

-- 1. 组织/团队
CREATE TABLE organizations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  plan TEXT DEFAULT 'free' CHECK (plan IN ('free', 'pro', 'enterprise')),
  created_at TIMESTAMPTZ DEFAULT now()
);

-- 2. 用户-组织关联 (多租户)
CREATE TABLE organization_members (
  organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
  role TEXT DEFAULT 'member' CHECK (role IN ('owner', 'admin', 'member')),
  joined_at TIMESTAMPTZ DEFAULT now(),
  PRIMARY KEY (organization_id, user_id)
);

-- 3. 订阅管理
CREATE TABLE subscriptions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
  stripe_customer_id TEXT,
  stripe_subscription_id TEXT,
  status TEXT DEFAULT 'active' CHECK (status IN ('active', 'past_due', 'canceled')),
  current_period_end TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- 4. 审计日志
CREATE TABLE audit_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
  actor_id UUID REFERENCES auth.users(id),
  action TEXT NOT NULL,
  resource_type TEXT NOT NULL,
  resource_id UUID,
  metadata JSONB,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- RLS 策略:组织隔离
ALTER TABLE organizations ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view own organizations"
  ON organizations FOR SELECT
  USING (
    id IN (
      SELECT organization_id FROM organization_members
      WHERE user_id = auth.uid()
    )
  );

CREATE POLICY "Only owners can delete organizations"
  ON organizations FOR DELETE
  USING (
    EXISTS (
      SELECT 1 FROM organization_members
      WHERE organization_id = organizations.id
        AND user_id = auth.uid()
        AND role = 'owner'
    )
  );

2.3 实时协作模板

// Supabase 实时协作模板
// 支持: 多人同时编辑、实时聊天、协同白板

import { createClient } from "@supabase/supabase-js"
import type { RealtimeChannel } from "@supabase/supabase-js"

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!
)

// 1. Presence:追踪谁在线
interface PresenceState {
  user_id: string
  user_name: string
  cursor?: { x: number; y: number }
  selection?: string[]
}

const channel: RealtimeChannel = supabase.channel("room-123", {
  config: {
    presence: {
      key: supabase.auth.currentUser?.id || "anonymous",
    },
  },
})

// 加入房间
channel.on("presence", { event: "sync" }, () => {
  const state = channel.presenceState()
  console.log("Online users:", Object.keys(state))
})

// 监听他人加入/离开
channel.on("presence", { event: "join" }, ({ key, newPresences }) => {
  console.log(`${key} joined with:`, newPresences)
})

channel.on("presence", { event: "leave" }, ({ key, leftPresences }) => {
  console.log(`${key} left`)
})

// 广播光标位置
document.addEventListener("mousemove", (e) => {
  channel.presenceTrack({
    cursor: { x: e.clientX, y: e.clientY },
    timestamp: Date.now(),
  })
})

await channel.subscribe(async (status) => {
  if (status === "SUBSCRIBED") {
    await channel.presenceTrack({ name: "Alice" })
  }
})

// 2. Broadcast:实时消息
channel.on("broadcast", { event: "chat-message" }, ({ payload }) => {
  console.log("Received:", payload)
  appendMessage(payload)
})

// 发送消息
const sendMessage = (content: string) => {
  channel.send({
    type: "broadcast",
    event: "chat-message",
    payload: {
      user_id: supabase.auth.currentUser?.id,
      content,
      sent_at: new Date().toISOString(),
    },
  })
}

// 3. Postgres Changes:监听数据库变更
channel.on(
  "postgres_changes",
  {
    event: "UPDATE",
    schema: "public",
    table: "documents",
    filter: "room_id=eq.123",
  },
  (payload) => {
    console.log("Document updated:", payload.new)
    // 实时同步文档内容
    syncDocument(payload.new)
  }
)

三、熔断器模式:高可用保障

3.1 为什么需要熔断器?

// 分布式系统的级联失败
// 
// 问题场景:
// - 外部支付 API 响应慢 (500ms → 30s)
// - 支付服务占满连接池
// - 其他依赖数据库的服务开始超时
// - 最终整个系统崩溃

// 解决方案: 熔断器
// 1. 正常状态: 请求直接通过
// 2. 失败率超过阈值: 熔断器打开 (快速失败,不调用外部服务)
// 3. 半开状态: 允许少量请求试探
// 4. 恢复: 逐步恢复调用

import { CircuitBreaker, CircuitBreakerState } from "./circuit-breaker"

// Supabase 内置熔断器
const breaker = new CircuitBreaker({
  name: "stripe-api",
  
  // 失败率阈值
  threshold: 0.5,        // 50% 失败率触发熔断
  
  // 最小请求数
  minimumRequests: 10,
  
  // 熔断持续时间
  duration: 30_000,      // 30 秒
  
  // 半开状态探测比例
  probeRatio: 0.3,       // 30% 请求允许通过探测
  
  onStateChange: (state: CircuitBreakerState) => {
    console.log(`Circuit ${state}`)
    // 通知监控
    metrics.track("circuit_breaker", { name: "stripe-api", state })
  },
})

// 在边缘函数中使用
Deno.serve(async (req) => {
  const result = await breaker.execute(async () => {
    // 调用外部服务
    const response = await fetch("https://api.stripe.com/v1/charges", {
      method: "POST",
      headers: { Authorization: `Bearer ${Deno.env.get("STRIPE_SECRET_KEY")}` },
      body: new URLSearchParams({ amount: 1000, currency: "usd" }),
    })
    
    if (!response.ok) throw new Error(`Stripe error: ${response.status}`)
    return response.json()
  })
  
  // 熔断状态下的降级处理
  if (result.err instanceof CircuitBreakerOpenError) {
    return Response.json(
      { error: "Payment service temporarily unavailable", code: "SERVICE_DEGRADED" },
      { status: 503 }
    )
  }
  
  return Response.json(result.value)
})

3.2 Supabase 边缘函数熔断配置

// supabase/functions/payment/index.ts
// 支付边缘函数,内置熔断与降级

const CONFIG = {
  // Stripe 调用超时
  stripeTimeout: 5000,
  
  // 熔断器配置
  circuitBreaker: {
    enabled: true,
    errorThreshold: 0.3,       // 30% 错误率
    resetTimeout: 30000,       // 30 秒后重试
    halfOpenMaxCalls: 3,      // 半开状态最多 3 个请求
  },
  
  // 降级策略
  fallback: {
    enabled: true,
    queuePayments: true,      // 队列延迟处理
    emailNotification: true,  // 通知管理员
  },
}

// 支付队列 (用于降级)
const paymentQueue: Array<{
  amount: number
  currency: string
  customerId: string
  timestamp: number
}> = []

// 熔断器状态检查
async function processPayment(amount: number, currency: string, customerId: string) {
  try {
    const response = await fetch("https://api.stripe.com/v1/charges", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${Deno.env.get("STRIPE_SECRET_KEY")}`,
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({ amount, currency, customer: customerId }),
      signal: AbortSignal.timeout(CONFIG.stripeTimeout),
    })
    
    if (!response.ok) {
      throw new Error(`Stripe API error: ${response.status}`)
    }
    
    return await response.json()
  } catch (error) {
    // 触发熔断器
    return handleCircuitBreakerError(error, amount, currency, customerId)
  }
}

async function handleCircuitBreakerError(error: Error, amount: number, currency: string, customerId: string) {
  // 记录错误
  await supabase.from("payment_errors").insert({
    error_type: error.name,
    error_message: error.message,
    amount,
    currency,
    customer_id: customerId,
    circuit_state: "open",
  })
  
  // 降级:队列支付,稍后重试
  if (CONFIG.fallback.queuePayments) {
    paymentQueue.push({ amount, currency, customerId, timestamp: Date.now() })
    
    // 触发通知
    if (CONFIG.fallback.emailNotification) {
      await sendAlertEmail(`Payment queued: ${amount} ${currency}`)
    }
    
    return {
      status: "queued",
      queue_id: crypto.randomUUID(),
      message: "Payment queued for processing",
    }
  }
  
  throw error
}

// 定时处理队列中的支付 (边缘函数定时器)
Deno.serve(async (req) => {
  if (req.url.includes("/process-queue")) {
    return await processPaymentQueue()
  }
  
  // 主支付处理
  const { amount, currency, customerId } = await req.json()
  return await processPayment(amount, currency, customerId)
})

四、向量存储:RAG 的新基础设施

4.1 pgvector 架构

-- Supabase 向量存储配置

-- 1. 启用 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;

-- 2. 创建文档表
CREATE TABLE documents (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  content TEXT NOT NULL,
  metadata JSONB DEFAULT '{}',
  
  -- 向量字段 (支持 1536 维 OpenAI, 1024 维 Cohere, 768 维 BERT)
  embedding vector(1536),
  
  -- 元数据: 来源、分区、标签等
  source TEXT,
  category TEXT,
  tags TEXT[],
  
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);

-- 3. 创建向量索引 (HNSW 算法,性能最佳)
CREATE INDEX ON documents 
  USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

-- 4. 相似度搜索
-- 余弦相似度 (Cosine Similarity): 适用于 OpenAI embeddings
-- 欧氏距离 (L2): 适用于图片向量
-- 内积 (IP): 适用于归一化向量

-- 查询最近似文档
SELECT 
  id,
  content,
  metadata,
  -- 计算相似度分数 (1 - 余弦距离 = 相似度)
  1 - (embedding <=> $1::vector) AS similarity,
  source
FROM documents
WHERE category = $2  -- 元数据过滤
ORDER BY embedding <=> $1::vector  -- HNSW 索引排序
LIMIT $3;

-- 5. 混合搜索 (向量 + 全文)
SELECT 
  id,
  content,
  1 - (embedding <=> $1::vector) AS vector_score,
  ts_rank(search_vector, plainto_tsquery('english', $2)) AS text_score,
  -- 综合评分
  (1 - (embedding <=> $1::vector)) * 0.7 + 
  ts_rank(search_vector, plainto_tsquery('english', $2)) * 0.3 AS combined_score
FROM documents
WHERE 
  search_vector @@ plainto_tsquery('english', $2)  -- 全文过滤
  AND category = $3                                   -- 分类过滤
ORDER BY combined_score DESC
LIMIT $4;

4.2 完整 RAG 管道实现

// supabase/functions/rag-chat/index.ts
// RAG 对话管道

import { createClient } from "@supabase/supabase-js"
import OpenAI from "openai"

const supabase = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
)

const openai = new OpenAI({
  apiKey: Deno.env.get("OPENAI_API_KEY"),
})

interface RAGRequest {
  query: string
  systemPrompt?: string
  maxContextDocuments?: number
  temperature?: number
}

Deno.serve(async (req: Request) => {
  const { query, systemPrompt, maxContextDocuments = 5, temperature = 0.7 } = await req.json() as RAGRequest
  
  // 1. 将用户问题转为向量
  const queryEmbeddingResponse = await openai.embeddings.create({
    model: "text-embedding-3-small",
    input: query,
  })
  const queryEmbedding = queryEmbeddingResponse.data[0].embedding
  
  // 2. 向量相似度检索
  const { data: contextDocuments, error } = await supabase.rpc("match_documents", {
    query_embedding: queryEmbedding,
    match_threshold: 0.7,      // 相似度阈值
    match_count: maxContextDocuments,
    filter_category: null,     // 可选:按分类过滤
  })
  
  if (error) {
    return Response.json({ error: error.message }, { status: 500 })
  }
  
  // 3. 构建上下文
  const context = contextDocuments
    .map((doc: { content: string; source: string }, i: number) => 
      `[${i + 1}] ${doc.content}\n来源: ${doc.source}`
    )
    .join("\n\n")
  
  // 4. 构建 RAG prompt
  const system = systemPrompt || 
    `你是一个有帮助的AI助手。请根据以下上下文信息回答用户的问题。
如果上下文中没有相关信息,请如实说明,不要编造答案。

---
上下文信息:
${context}
---`

  // 5. 调用 LLM
  const completion = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages: [
      { role: "system", content: system },
      { role: "user", content: query },
    ],
    temperature,
    stream: false,
  })
  
  const answer = completion.choices[0].message.content!
  
  // 6. 记录对话历史 (可选)
  await supabase.from("rag_conversations").insert({
    query,
    answer,
    context_doc_ids: contextDocuments.map((d: { id: string }) => d.id),
    model: "gpt-4-turbo",
  })
  
  return Response.json({
    answer,
    context_used: contextDocuments.length,
    sources: contextDocuments.map((d: { source: string }) => d.source),
  })
})

// 数据库函数: 向量检索
// 需要在 Supabase SQL Editor 中创建
/*
CREATE OR REPLACE FUNCTION match_documents(
  query_embedding vector(1536),
  match_threshold float DEFAULT 0.7,
  match_count int DEFAULT 5,
  filter_category text DEFAULT NULL
)
RETURNS TABLE (
  id uuid,
  content text,
  metadata jsonb,
  source text,
  category text,
  similarity float
)
LANGUAGE plpgsql
AS $$
BEGIN
  RETURN QUERY
  SELECT
    documents.id,
    documents.content,
    documents.metadata,
    documents.source,
    documents.category,
    1 - (documents.embedding <=> query_embedding) AS similarity
  FROM documents
  WHERE
    (filter_category IS NULL OR documents.category = filter_category)
    AND 1 - (documents.embedding <=> query_embedding) > match_threshold
  ORDER BY documents.embedding <=> query_embedding
  LIMIT match_count;
END;
$$
*/

4.3 性能优化

-- pgvector 性能调优

-- 1. 索引选择
-- HNSW: 更高召回率,更高构建时间,适合读取密集型
-- IVFFlat: 更低内存,更快构建,适合更新频繁型

-- HNSW 参数调优
CREATE INDEX documents_embedding_hnsw ON documents
  USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 128);  -- 更高召回

-- 2. 分区策略 (大规模向量)
CREATE TABLE documents_partitioned (
  id UUID DEFAULT gen_random_uuid(),
  embedding vector(1536),
  content TEXT,
  created_at TIMESTAMPTZ DEFAULT now()
) PARTITION BY RANGE (created_at);

-- 按月分区
CREATE TABLE documents_2026_01 PARTITION OF documents_partitioned
  FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

-- 3. 量化压缩 (降低内存)
-- int8 量化: 精度损失约 5-10%,内存减少 4x
ALTER TABLE documents ALTER COLUMN embedding TYPE vector(1536);

-- 4. 监控查询性能
SELECT 
  query,
  calls,
  total_exec_time / 1000 AS total_seconds,
  mean_exec_time AS avg_ms,
  stddev_exec_time
FROM pg_stat_statements
WHERE query LIKE '%<=%'
ORDER BY total_exec_time DESC
LIMIT 10;

五、实时数据订阅:<100ms 延迟

5.1 实时架构

// Supabase Realtime 工作原理
// 
// 架构: Postgres WAL → Decoder → Broadcast → WebSocket
//       ┌─────────────────────────────────────────────────────────┐
//       │                    PostgreSQL                           │
//       │  ┌──────────┐  ┌───────────┐  ┌───────────────────┐   │
//       │  │ WAL Log   │→ │ pg_logical│→ │ Supabase Realtime │   │
//       │  │ Write-Ahead│ │ Decoder  │  │    Broadcast      │   │
//       │  │ Log      │  └───────────┘  │                   │   │
//       │  └──────────┘                  │  ┌─────────────┐  │   │
//       └────────────────────────────────│  │ WebSocket   │  │   │
//                                         │  │ Server      │  │   │
//                                         │  └──────┬──────┘  │   │
//                                         └─────────│──────────┘   │
//                                                   │              │
//                                         ┌─────────▼──────────┐   │
//                                         │    Client SDK     │   │
//                                         │  (JavaScript/...) │   │
//                                         └───────────────────┘   │
//
// 延迟分析:
// - WAL 写入: ~1ms
// - Decoder 解析: ~5ms
// - Broadcast 广播: ~10ms
// - 网络传输: 取决于地理距离 (边缘节点 <10ms)
// 
// 端到端延迟: 15-100ms (远优于轮询的 1000-5000ms)

5.2 完整实时聊天实现

// 实时聊天应用

interface Message {
  id: string
  room_id: string
  sender_id: string
  content: string
  created_at: string
}

interface Reaction {
  message_id: string
  emoji: string
  user_ids: string[]
}

// 1. 订阅新消息
const channel = supabase
  .channel(`room:${roomId}`)
  .on(
    "postgres_changes",
    {
      event: "INSERT",
      schema: "public",
      table: "messages",
      filter: `room_id=eq.${roomId}`,
    },
    (payload) => {
      const message: Message = payload.new
      appendMessage(message)
      
      // 通知
      if (message.sender_id !== currentUserId) {
        showNotification(`${message.sender_name}: ${message.content}`)
      }
    }
  )
  .on(
    "postgres_changes",
    {
      event: "UPDATE",
      schema: "public",
      table: "reactions",
    },
    (payload) => {
      updateReactions(payload.new as Reaction)
    }
  )
  .subscribe((status) => {
    console.log("Channel status:", status)
  })

// 2. 发送消息
async function sendMessage(content: string) {
  const { data, error } = await supabase
    .from("messages")
    .insert({
      room_id: roomId,
      sender_id: currentUserId,
      content,
    })
    .select()
    .single()
  
  if (error) {
    console.error("Failed to send:", error)
    showRetryOption()
  }
  
  return data
}

// 3. 表情反应
async function addReaction(messageId: string, emoji: string) {
  const { data, error } = await supabase.rpc("toggle_reaction", {
    p_message_id: messageId,
    p_emoji: emoji,
    p_user_id: currentUserId,
  })
}

// 4. Typing 指示器
const typingChannel = supabase.channel(`typing:${roomId}`)

typingChannel.on("broadcast", { event: "typing" }, ({ payload }) => {
  if (payload.user_id !== currentUserId) {
    showTypingIndicator(payload.user_name)
  }
})

// 广播 typing 状态
let typingTimeout: ReturnType<typeof setTimeout>
input.addEventListener("input", () => {
  typingChannel.send({
    type: "broadcast",
    event: "typing",
    payload: { user_id: currentUserId, user_name: currentUserName },
  })
  
  clearTimeout(typingTimeout)
  typingTimeout = setTimeout(() => {
    // 用户停止输入
  }, 2000)
})

六、身份认证:GoTrue 深度整合

6.1 多方式认证

// Supabase Auth 配置

// 1. 邮箱密码认证
const { data, error } = await supabase.auth.signUp({
  email: "user@example.com",
  password: "secure-password",
  options: {
    data: { name: "Alice" },
    emailRedirectTo: "https://myapp.com/confirm",
  },
})

// 2. 魔法链接 (无密码登录)
const { error } = await supabase.auth.signInWithOtp({
  email: "user@example.com",
  options: {
    emailRedirectTo: "https://myapp.com/confirm",
  },
})

// 3. 社交登录
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: "github",  // 支持: github, google, apple, discord, twitter...
  options: {
    redirectTo: "https://myapp.com/auth/callback",
    scopes: "read:user user:email",  // 请求权限
  },
})

// 4. 验证邮箱
const { data, error } = await supabase.auth.verifyOtp({
  email: "user@example.com",
  token: "123456",
  type: "email",
})

// 5. JWT 会话管理
const { data: { session }, error } = await supabase.auth.getSession()

// 访问令牌 (用于 API 调用)
const accessToken = session.access_token

// 刷新令牌 (自动续期)
const refreshToken = session.refresh_token

// 监听会话变化
supabase.auth.onAuthStateChange((event, session) => {
  if (event === "SIGNED_IN") {
    console.log("User signed in:", session.user)
  } else if (event === "SIGNED_OUT") {
    console.log("User signed out")
    redirectToLogin()
  } else if (event === "TOKEN_REFRESHED") {
    console.log("Token refreshed")
  }
})

6.2 RLS 与认证深度整合

-- Row Level Security (行级安全) 策略

-- 1. 基础: 用户只能访问自己的数据
CREATE POLICY "Users can read own profile"
  ON profiles FOR SELECT
  USING (auth.uid() = user_id);

-- 2. 中级: 用户可以读取团队成员的数据
CREATE POLICY "Team members can read project data"
  ON projects FOR SELECT
  USING (
    team_id IN (
      SELECT team_id FROM team_members
      WHERE user_id = auth.uid()
    )
  );

-- 3. 高级: 基于角色的细粒度访问
CREATE POLICY "Admins can manage all users"
  ON users FOR ALL
  USING (
    EXISTS (
      SELECT 1 FROM organization_members
      WHERE organization_id = users.organization_id
        AND user_id = auth.uid()
        AND role IN ('admin', 'owner')
    )
  );

-- 4. 公开数据 (无需认证)
CREATE POLICY "Public profiles are visible to everyone"
  ON profiles FOR SELECT
  USING (is_public = true);

-- 5. 条件更新 (只能修改自己的数据)
CREATE POLICY "Users can update own posts"
  ON posts FOR UPDATE
  USING (author_id = auth.uid())
  WITH CHECK (author_id = auth.uid());

-- 6. 订阅系统: 根据订阅状态限制访问
CREATE POLICY "Pro users can access pro features"
  ON features FOR SELECT
  USING (
    organization_id IN (
      SELECT o.id FROM organizations o
      JOIN subscriptions s ON s.organization_id = o.id
      WHERE s.status = 'active' AND o.plan IN ('pro', 'enterprise')
    )
    OR
    -- 公开特性对所有人可见
    is_public = true
  );

七、生产环境部署

7.1 自托管 vs 云服务

# docker-compose.yml (自托管 Supabase)

version: "3.8"

services:
  postgres:
    image: supabase/postgres:15.6.0
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: postgres
      POSTGRES_USER: supabase_admin
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  kong:
    image: kong:3.4
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /usr/local/kong.yml
      KONG_PLUGINS: bundled,cors,acl
    volumes:
      - ./kong.yml:/usr/local/kong.yml
    depends_on:
      - auth
      - rest

  auth:
    image: supabase/gotrue:v2.151.0
    environment:
      GOTRUE_SITE_URL: ${SITE_URL}
      GOTRUE_URI_ALLOWLIST: ${ALLOWED_ORIGINS}
      GOTRUE_DISABLE_SIGNUP: "false"
      DATABASE_URL: postgres://supabase_admin:${POSTGRES_PASSWORD}@postgres:5432/postgres?sslmode=disable
      JWT_SECRET: ${JWT_SECRET}
      SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}
      SMTP_HOST: ${SMTP_HOST}
      SMTP_PORT: ${SMTP_PORT}

  storage:
    image: supabase/storage-api:v1.16.0
    environment:
      ANON_KEY: ${SUPABASE_ANON_KEY}
      SERVICE_KEY: ${SUPABASE_SERVICE_KEY}
      DATABASE_URL: postgres://supabase_admin:${POSTGRES_PASSWORD}@postgres:5432/postgres?sslmode=disable
    volumes:
      - storage:/var/lib/storage

  realtime:
    image: supabase/realtime:v2.28.0
    environment:
      DB_HOST: postgres
      DB_PORT: 5432
      DB_USER: supabase_admin
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      DB_NAME: postgres
      PORT: 4000

  functions:
    image: supabase/edge-runtime:v1.55.0
    environment:
      SUPABASE_URL: http://kong:8000
      SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
      SUPABASE_SERVICE_KEY: ${SUPABASE_SERVICE_KEY}

volumes:
  pgdata:
  storage:

7.2 性能监控

// Supabase 性能监控

// 1. 数据库查询分析
const { data, error } = await supabase.rpc("explain_query", {
  sql: "SELECT * FROM messages WHERE room_id = $1 ORDER BY created_at DESC LIMIT 20",
  params: [roomId],
})

// 2. 实时性能指标
interface PerformanceMetrics {
  queryTime: number      // 查询执行时间
  connectionPoolUsage: number  // 连接池使用率
  replicationLag: number  // 复制延迟
  cacheHitRate: number    // 缓存命中率
}

// 3. 边缘函数监控
Deno.serve(async (req) => {
  const start = performance.now()
  
  // 处理请求
  const result = await processRequest(req)
  
  const duration = performance.now() - start
  
  // 发送到监控
  await supabase.from("function_metrics").insert({
    function_name: "rag-chat",
    duration_ms: duration,
    status: result.success ? "success" : "error",
    error_message: result.error,
    timestamp: new Date().toISOString(),
  })
  
  return Response.json(result)
})

八、总结

Supabase 在 2026 年已经进化为一套完整的企业级后端平台:

  1. PostgreSQL 核心: 关系型数据、向量存储、时序数据、全文搜索,全部基于 SQL
  2. AI 时代支持: pgvector、RAG 管道、流式推理,与 LLM 深度整合
  3. 实时能力: <100ms 延迟的 WebSocket 订阅,覆盖聊天、协作、实时数据
  4. 模板系统: SaaS、社交、RAG 等场景开箱即用,开发效率提升 300%
  5. 高可用保障: 熔断器模式、服务降级、边缘函数容错
  6. 安全可靠: RLS 行级安全、GoTrue 认证、审计日志

对于创业团队和独立开发者来说,Supabase 提供了一条从原型到生产的最快路径——用一个开源平台替代过去需要 10+ 个服务才能实现的全部功能。


参考资料

推荐文章

Vue3中的JSX有什么不同?
2024-11-18 16:18:49 +0800 CST
Linux查看系统配置常用命令
2024-11-17 18:20:42 +0800 CST
Shell 里给变量赋值为多行文本
2024-11-18 20:25:45 +0800 CST
filecmp,一个Python中非常有用的库
2024-11-19 03:23:11 +0800 CST
JavaScript 策略模式
2024-11-19 07:34:29 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
使用 `nohup` 命令的概述及案例
2024-11-18 08:18:36 +0800 CST
随机分数html
2025-01-25 10:56:34 +0800 CST
404错误页面的HTML代码
2024-11-19 06:55:51 +0800 CST
FastAPI 入门指南
2024-11-19 08:51:54 +0800 CST
手机导航效果
2024-11-19 07:53:16 +0800 CST
百度开源压测工具 dperf
2024-11-18 16:50:58 +0800 CST
JavaScript数组 splice
2024-11-18 20:46:19 +0800 CST
WebSQL数据库:HTML5的非标准伴侣
2024-11-18 22:44:20 +0800 CST
程序员茄子在线接单