用途
该技能是一份完整指南,用于将 Supabase 与 Next.js 13+ 的 App Router 集成,涵盖认证、数据库设置、行级安全(RLS)以及生产应用中的常见模式。适用于启动带有 Supabase 的 Next.js 新项目、需要社交登录认证、构建用户专属数据应用(如笔记、收藏)、需要实时订阅、需要安全的服务端与客户端数据访问、或用数据库实现限流与审计日志、构建 SaaS 应用等场景。
核心设置
第 1 步:安装依赖
npm install @supabase/supabase-js @supabase/ssr
# 或
pnpm add @supabase/supabase-js @supabase/ssr
第 2 步:环境变量
创建 .env.local:
NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
获取位置:https://app.supabase.com/project/_/settings/api
第 3 步:创建 Supabase 客户端
服务端客户端(lib/supabase/server.ts)使用 createServerClient 与 next/headers 的 cookies,通过 getAll() 读取 cookie、setAll() 写入 cookie(写入放在 try/catch 中,注明从 Server Component 调用时由中间件处理)。浏览器客户端(lib/supabase/client.ts)使用 createBrowserClient。
第 4 步:认证设置
提供 Auth Context(contexts/auth-context.tsx),声明 user、loading、signIn、signUp、signOut,通过 supabase.auth.getSession() 获取初始会话,并用 onAuthStateChange 监听认证变化,在清理函数中调用 subscription.unsubscribe();signIn 使用 signInWithPassword,signUp 使用 signUp,signOut 使用 signOut。并导出 useAuth,在 Provider 外使用时抛出错误。
第 5 步:数据库表结构示例
给出四张表的建表与索引语句:video_analyses(视频分析,含 youtube_id、user_id、title、author、thumbnail_url、duration、transcript、topics、summary、suggested_questions 等,并对 youtube_id、user_id 建索引);notes(笔记,user_id 非空,video_id 外键带 ON DELETE CASCADE,含 source、source_id、text、metadata,对 user_id、video_id 建索引);user_favorites(收藏,user_id 与 video_analysis_id 组合主键,均级联删除);rate_limits(限流,含 key、identifier、timestamp,对 key+timestamp 与 timestamp 建索引)。
第 6 步:行级安全(RLS)
对 notes 和 user_favorites 启用 RLS,并创建策略:笔记的查看、创建、更新、删除均以 auth.uid() = user_id 为条件(SELECT/UPDATE/DELETE 用 USING,INSERT 用 WITH CHECK);收藏的查看、添加、删除同样以 auth.uid() = user_id 为条件。
使用示例
- 示例 1:服务端数据获取:在
app/my-notes/page.tsx中用服务端客户端取getUser(),未登录则 redirect 到 /login,再查询notes表按 user_id 过滤并按 created_at 倒序。 - 示例 2:客户端数据变更:在客户端组件中用
useAuth获取用户,通过表单提交向notes表 insert(带 user_id、video_id、source、text),并检查 error。 - 示例 3:带鉴权的 API 路由:
app/api/notes/route.ts中 GET 与 POST 均先getUser(),未授权返回 401;GET 按 userId 与 videoId 查询,POST 解析 body 后 insert 并.select().single(),出错返回 500。 - 示例 4:实时订阅:客户端组件先取初始笔记,再用
supabase.channel('notes-changes').on('postgres_changes', ...)订阅,按user_id=eq.${user.id}过滤,依据 INSERT/DELETE/UPDATE 事件更新本地 state,并在清理函数中channel.unsubscribe()。 - 示例 5:社交登录:使用
signInWithOAuth支持 Google 与 GitHub,redirectTo指向/auth/callback;回调路由中读取 code 并调用exchangeCodeForSession(code),随后重定向到根路径。
最佳实践
始终使用 RLS(绝不信任客户端安全);分离客户端(敏感操作用服务端客户端);为数据类型化;处理认证状态(用 context 管理用户状态);清理订阅(在 useEffect 清理中取消订阅);多表操作用事务;为外键建索引;注意常见陷阱:忘记 RLS 导致数据暴露、在浏览器代码中使用错误的客户端、不处理 error 对象、订阅泄漏、缺少索引导致大表查询慢、在 RLS 策略中硬编码 ID 而非使用 auth.uid()。
数据库模式
- 软删除:为 notes 增加
deleted_at列,并在 SELECT 策略中加入deleted_at IS NULL。 - 审计追踪:创建
audit_logs表,含 user_id、action、table_name、record_id、old_data、new_data、created_at。 - 自动时间戳:创建
update_updated_at()触发器函数,更新时设置NEW.updated_at = NOW(),并在 notes 的 BEFORE UPDATE 上挂载触发器。
类型生成
安装 Supabase CLI(npm install -g supabase),运行 supabase gen types typescript --project-id your-project-id > lib/database.types.ts 生成类型;随后可从 Database 中派生 Row、Insert、Update 类型。
后续步骤
设置数据库迁移、为文件配置存储桶、为认证添加邮件模板、为复杂逻辑设置 Edge Functions、配置自定义 SMTP、添加数据库备份。
相关技能
保护 Next.js API 路由、类型安全的表单验证、弹性异步操作。