PolarDB Supabase最佳实践-Web应用

PolarDB SupabasePolarDB PostgreSQL推出全托管的Supabase服务,以PolarDB PostgreSQL为核心,整合了Realtime实时数据库、RESTful API、GoTrue身份认证、文件存储、日志采集等关键功能,并在此基础上进行了优化与增强,为您省去Supabase复杂的参数管理和应用运维等操作,并提供了兼具灵活性与高性能的后端解决方案。您可以基于PolarDB Supabase快速搭建Web、SaaS平台、AI集成应用等现代化应用。

接下来,将为您展示如何使用PolarDB Supabase快速搭建一个会议笔记系统。

效果展示

交互体验如下,打开两个浏览器,进入同一个会议,可以观察到两侧的信息是实时同步的。会议笔记系统的核心功能包括实时协作编辑、用户在线状态管理、文件上传与管理、任务追踪、活动日志以及标签系统。

PixPin_2025-07-11_13-54-49

核心功能

功能

说明

完整数据库功能

Supabase基于PolarDB PostgreSQL数据库构建,提供完整的数据库功能:

  • 关系型数据库: 支持复杂的表关系、外键约束、事务处理。

  • JSONB支持: 存储半结构化数据,如笔记内容、活动数据。

  • 全文搜索: 内置全文搜索功能。

  • 扩展支持: 支持PostgreSQL扩展,如UUID生成、时间戳处理。

-- 示例:使用JSONB存储复杂数据
CREATE TABLE notes (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  content JSONB DEFAULT '{}',  -- 存储富文本内容
  activity_data JSONB DEFAULT '{}'  -- 存储活动元数据
);

实时订阅(Realtime)

基于PostgreSQL的逻辑复制,实现实时数据同步:

  • 数据库变更监听: 监听INSERT、UPDATE、DELETE事件。

  • 频道管理: 支持多个独立频道,按需订阅。

  • 事件过滤: 通过条件过滤减少不必要的事件。

  • 自动重连: 网络断开时自动重连。

// 实时订阅示例
const channel = supabase
  .channel('meeting-updates')
  .on('postgres_changes', {
    event: 'UPDATE',
    schema: 'public',
    table: 'notes',
    filter: 'meeting_id=eq.123'
  }, (payload) => {
    console.log('笔记已更新:', payload.new)
  })
  .subscribe()

身份认证(Auth)

内置完善的身份认证和授权系统:

  • 多种登录方式: 邮箱密码、社交登录、魔法链接。

  • 会话管理: 自动处理token刷新、会话持久化。

  • 用户管理: 用户注册、密码重置、邮箱验证。

  • 匿名用户: 支持临时用户访问。

// 认证示例
const { data: { user }, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'password'
})

行级安全(RLS)

基于PostgreSQL的行级安全策略:

  • 细粒度权限控制: 基于用户、角色、数据条件控制访问。

  • 策略定义: 使用SQL定义访问规则。

  • 自动应用: 所有查询自动应用安全策略。

  • 性能优化: 在数据库层面过滤数据。

-- RLS策略示例
CREATE POLICY "Users can only see their own meetings" ON meetings
  FOR SELECT USING (auth.uid() = created_by);

CREATE POLICY "Users can update their own meetings" ON meetings
  FOR UPDATE USING (auth.uid() = created_by);

存储服务(PolarFS)

基于PolarDB文件系统(Polar File System,简称PolarFS)进行文件存储与管理:

  • 文件上传: 支持大文件上传、断点续传。

  • 访问控制: 基于RLS的文件访问权限。

  • 文件管理: 文件夹组织、元数据管理。

// 文件上传示例
const { data, error } = await supabase.storage
  .from('meeting-files')
  .upload('document.pdf', file, {
    cacheControl: '3600',
    upsert: false
  })

边缘函数(Edge Functions)

基于Deno的服务器端函数:

  • TypeScript支持: 原生TypeScript支持。

  • 数据库访问: 直接访问Supabase数据库。

  • 第三方集成: 调用外部API、处理webhook。

// 边缘函数示例
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

serve(async (req) => {
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_ANON_KEY') ?? ''
  )
  
  const { data } = await supabase.from('meetings').select('*')
  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' }
  })
})

API(REST & GraphQL)

自动生成的API接口:

  • REST API: 自动生成CRUD接口。

  • GraphQL: 支持GraphQL查询(企业版)。

  • 实时API: 支持实时查询和订阅。

  • 类型安全: TypeScript类型。

// REST API示例
const { data, error } = await supabase
  .from('meetings')
  .select('*, notes(*)')
  .eq('id', meetingId)
  .single()

搭建会议笔记系统

在会议笔记系统中,将主要应用以下功能:

功能

说明

PolarDB PostgreSQL版集群

数据存储,主要记录会议、笔记、任务、用户状态等表信息。

实时订阅(Realtime)

协作功能,用于实时同步笔记编辑、用户在线状态。

行级安全(RLS)

数据安全,用于控制用户访问权限。

存储服务(PolarFS)

文件管理,用于会议资料上传和下载。

身份认证(Auth)

用户管理,用于管理用户登录和会话管理。

应用技术栈

  • 前端:Next.js 15 + React 18 + TypeScript。

  • 后端PolarDB Supabase (PostgreSQL + 鉴权 + 实时订阅 + 存储)。

  • UI:Tailwind CSS + Radix UI。

  • 状态管理:React Hooks + 本地状态。

数据库设计

为会议笔记系统的核心功能设计会议表、笔记表、用户在线状态表、标签表及任务表等系统表,并通过外键约束以确保数据的一致性。

SQL示例

-- 创建会议表
CREATE TABLE IF NOT EXISTS meetings (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  description TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- 创建笔记表
CREATE TABLE IF NOT EXISTS notes (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  meeting_id UUID REFERENCES meetings(id) ON DELETE CASCADE,
  content JSONB DEFAULT '{}',
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- 创建用户在线状态表
CREATE TABLE IF NOT EXISTS user_presence (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  meeting_id UUID REFERENCES meetings(id) ON DELETE CASCADE,
  user_name VARCHAR(100) NOT NULL,
  user_color VARCHAR(7) DEFAULT '#1890ff',
  is_typing BOOLEAN DEFAULT FALSE,
  last_seen TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  UNIQUE(meeting_id, user_name)
);

-- 创建标签表
CREATE TABLE IF NOT EXISTS tags (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  meeting_id UUID REFERENCES meetings(id) ON DELETE CASCADE,
  name VARCHAR(50) NOT NULL,
  color VARCHAR(7) DEFAULT '#1890ff',
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- 创建任务表
CREATE TABLE IF NOT EXISTS tasks (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  meeting_id UUID REFERENCES meetings(id) ON DELETE CASCADE,
  title VARCHAR(255) NOT NULL,
  description TEXT,
  assignee VARCHAR(100),
  status VARCHAR(20) DEFAULT 'pending',
  due_date TIMESTAMP WITH TIME ZONE,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- 创建会议活动日志表
CREATE TABLE IF NOT EXISTS meeting_activities (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  meeting_id UUID REFERENCES meetings(id) ON DELETE CASCADE,
  user_name VARCHAR(100) NOT NULL,
  activity_type VARCHAR(50) NOT NULL, -- 'join', 'leave', 'edit', 'add_tag', 'add_task', etc.
  activity_data JSONB DEFAULT '{}',
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- 会议资料表:meeting_files
CREATE TABLE IF NOT EXISTS meeting_files (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  meeting_id UUID REFERENCES meetings(id) ON DELETE CASCADE,
  file_name VARCHAR(255) NOT NULL,
  file_url TEXT NOT NULL,
  uploader VARCHAR(100),
  uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  file_size BIGINT,
  mime_type VARCHAR(100)
);

-- 为会议ID加索引
CREATE INDEX IF NOT EXISTS idx_meeting_files_meeting_id ON meeting_files(meeting_id);

实时订阅配置

启用实时功能

-- 启用实时订阅功能
ALTER PUBLICATION supabase_realtime ADD TABLE meetings;
ALTER PUBLICATION supabase_realtime ADD TABLE notes;
ALTER PUBLICATION supabase_realtime ADD TABLE user_presence;
ALTER PUBLICATION supabase_realtime ADD TABLE tags;
ALTER PUBLICATION supabase_realtime ADD TABLE tasks;
ALTER PUBLICATION supabase_realtime ADD TABLE meeting_activities;
ALTER PUBLICATION supabase_realtime ADD TABLE meeting_files; 

客户端实时订阅

// 创建自定义Hook管理实时订阅
export function useRealtime(meetingId: string, callbacks: RealtimeCallbacks) {

  const channelsRef = useRef<any[ ]>([ ])


  const cleanup = useCallback(() => {
    channelsRef.current.forEach((channel) => {
      supabase.removeChannel(channel)
    })
    channelsRef.current = []
  }, [])


  useEffect(() => {
    if (!meetingId) return

    // 清理之前的连接
    cleanup()

    // 创建多个专用频道
    const presenceChannel = supabase
      .channel(`presence:${meetingId}`)
      .on(
        "postgres_changes",
        {
          event: "*",
          schema: "public",
          table: "user_presence",
          filter: `meeting_id=eq.${meetingId}`,
        },
        (payload) => {
          console.log("User presence change:", payload)
          if (callbacks.onUserPresenceChange) {
            loadOnlineUsers()
          }
        },
      )
      .subscribe()

    // 保存频道引用用于清理
    channelsRef.current = [presenceChannel, /* 其他频道 */]
  }, [meetingId, callbacks])

  return { cleanup }
}

实时订阅设计指南

  • 功能隔离:按业务场景划分独立频道(如在线状态、文档协作、任务通知),避免逻辑耦合。

  • 事件过滤:通过filter参数限定事件范围(如指定会议ID),减少冗余数据传输。

  • 资源回收:在组件卸载或页面跳转时,主动调用removeChannel清理所有频道连接,释放资源。

  • 错误处理:监听订阅通道的状态,对断连、超时等异常进行重试或降级处理。

安全性

行级安全(RLS)

-- 启用RLS
ALTER TABLE meetings ENABLE ROW LEVEL SECURITY;

-- 创建安全策略
CREATE POLICY "Users can view all meetings" ON meetings
  FOR SELECT USING (true);

CREATE POLICY "Users can create meetings" ON meetings
  FOR INSERT WITH CHECK (true);

CREATE POLICY "Users can update their own meetings" ON meetings
  FOR UPDATE USING (auth.uid() = created_by);

用户认证

  const login = useCallback(async (email: string, password: string) => {
    try {
      const { data, error } = await supabase.auth.signInWithPassword({ email, password })
      if (error) throw error
      if (data.user) {
        const userData = transformSupabaseUser(data.user)
        setUser(userData)
        localStorage.setItem("meeting_user", JSON.stringify(userData))
        return { success: true, user: userData }
      }
      return { success: false, error: '登录失败' }
    } catch (error: any) {
      return { success: false, error: error.message || '登录失败' }
    }
  }, [])

  const transformSupabaseUser = (supabaseUser: SupabaseUser): User => ({
    id: supabaseUser.id,
    email: supabaseUser.email || null,
    name: supabaseUser.user_metadata?.name || supabaseUser.email?.split('@')[0] || '用户',
    avatar_url: supabaseUser.user_metadata?.avatar_url || null,
    created_at: supabaseUser.created_at,
    is_anonymous: supabaseUser.user_metadata?.is_anonymous || false,
  })

客户端集成

客户端配置

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

// 类型定义
export interface Meeting {
  id: string
  created_at: string
  title: string
  description: string | null
}

环境变量管理

# 生产环境变量
NEXT_PUBLIC_SUPABASE_URL=<YOUR_SUPABASE_PUBLIC_URL>
NEXT_PUBLIC_SUPABASE_ANON_KEY=<YOUR_SUPABASE_ANON_KEY>

快速搭建

  1. 下载项目示例代码PolarDB-Supabase-App-Demo

  2. 准备运行环境:项目示例代码需要在您的本地环境中安装Node.jspnpm

    说明
    • Node.js:请前往Node.js官方网站进行下载和安装。

    • pnpm:在安装Node.js后,可通过npm进行全局安装,命令为npm install -g pnpm

  3. 环境配置:在项目根目录创建.env.local文件,并将以下值替换为您PolarDB Supabase的配置。

    说明

    您可在集群的AI能力 > AI应用列表页面,单击您的应用ID进入应用详情页,在拓扑图配置页签中查看相关配置信息。

    • <YOUR_SUPABASE_PUBLIC_URL>为应用的公网地址

    • <YOUR_SUPABASE_ANON_KEY>为应用参数secret.jwt.anonKey的值。

    # 生产环境变量
    NEXT_PUBLIC_SUPABASE_URL=<YOUR_SUPABASE_PUBLIC_URL>
    NEXT_PUBLIC_SUPABASE_ANON_KEY=<YOUR_SUPABASE_ANON_KEY>
  4. 数据库初始化:在项目根目录的scripts文件夹下找到01-create-tables.sql文件,并在Supabase Dashboard上面的右侧导航栏SQL Editor中执行SQL。image

  5. 运行项目:请在项目根目录下执行以下命令以安装依赖并启动项目。启动后,默认的本地访问地址为http://localhost:3000,您可以直接在浏览器中访问该系统。

    pnpm install
    pnpm dev

    image

经验总结

基于上述会议笔记系统项目的实战经验,PolarDB Supabase最佳实践包括:

遵循这些最佳实践可以构建稳定、安全、高性能的PolarDB Supabase应用。