Supabase自建后端实战:PostgreSQL+RLS构建安全可控的全栈基础设施

发布时间:2026/7/16 3:39:25

Supabase自建后端实战:PostgreSQL+RLS构建安全可控的全栈基础设施 1. 项目概述为什么 Supabase 正在成为开发者自建后端的“默认选项”Supabase 不是另一个“又一个开源项目”它是过去三年里我亲手部署、调试、上线并维护过 7 个真实业务系统的后端服务中唯一一个让我在第 3 次部署时就彻底放弃写 Express PostgreSQL 脚手架的工具。它解决的不是“能不能跑”的问题而是“要不要再花三天搭鉴权、写 CRUD 接口、配 CORS、调 JWT 过期逻辑”的生存级痛点。核心关键词——Supabase、开源、Firebase、数据库、后端服务——全部落在一个极其现实的坐标上一个前端工程师用不到 20 分钟就能获得生产可用的用户注册登录、实时数据同步、RBAC 权限控制、文件存储和 Postgres 数据库管理能力且所有代码、配置、数据完全掌握在自己手里。这不是 Demo不是 PoC而是我在给本地社区图书馆做图书预约系统、为独立咖啡馆开发会员积分后台、帮非遗手作工作室搭建作品展示 CMS 时真正落地的选择。它不替代你对数据库原理的理解但能让你把“理解原理”的时间从反复造轮子挪到真正打磨业务逻辑上。适合谁三类人最受益一是前端转全栈想快速验证想法的开发者二是小团队或个体开发者没有专职后端但需要稳定可靠的后端支撑三是对数据主权、合规性有硬性要求的项目比如医疗健康类轻应用、教育类数据采集工具必须确保用户数据不出内网或指定云区域。它不是银弹但当你面对一个需要“今天能注册、明天能存数据、后天能查报表”的真实需求时Supabase 提供的是一条已被千百个项目验证过的、最短的可行路径。2. 核心设计思路与方案选型深度拆解2.1 为什么是 Supabase而不是自己从零搭一套“Firebase 风格”后端这个问题我问了自己整整两周。当时手头有个为老年大学开发的课程报名系统需求很明确学员微信扫码注册、老师后台审核、课程表实时更新、报名状态秒级同步。第一反应还是老路子NestJS TypeORM PostgreSQL JWT Redis 缓存 Nginx 反向代理。画架构图花了 40 分钟写 Docker Compose 文件写了 1 小时等 Postgres 初始化完成、Sequelize 迁移脚本跑通、JWT 密钥生成并注入环境变量、CORS 白名单配好……天已经黑了。第三天我卡在了一个极其荒谬的问题上微信小程序的wx.login()返回的code怎么安全地传给后端是走 HTTPS POST还是存在 localStorage 然后 AJAX还是用wx.requestPayment的回调机制光是这个“登录态如何安全建立”的环节就让我翻了 3 个 GitHub 仓库的 Issues读了 2 篇 Medium 上关于 OAuth2.1 的长文最后发现这根本不是业务逻辑而是基础设施的噪音。Supabase 的设计哲学恰恰就是把这类噪音直接物理隔离。它的核心不是“提供一堆 API”而是将 Postgres 数据库本身变成一个可编程的、带身份语义的网络服务。这背后有三个关键设计选择第一Postgres 作为唯一真相源Single Source of Truth。Firebase 的 Realtime Database 是 JSON 树Firestore 是文档它们都抽象掉了关系模型。而 Supabase 强制你使用标准 SQL 和关系范式。这意味着你不需要额外学一套查询 DSL你的SELECT * FROM users WHERE status active就是最终的 API 查询。更关键的是Postgres 的 Row Level SecurityRLS策略直接翻译成了前端可调用的权限规则。比如一条 RLS 策略USING (auth.uid() user_id)就等价于“每个用户只能读写自己的数据行”。这个策略写在数据库里由数据库引擎在查询执行前强制校验比任何应用层的if (user.id ! req.userId) throw new Error()都更底层、更可靠、也更难绕过。我见过太多项目因为应用层鉴权漏掉一个await或者一个return导致数据越权访问。RLS 把这个风险降到了数据库引擎级别。第二Auth 服务与数据库深度耦合而非独立微服务。Firebase Auth 是一个黑盒服务你调它的 SDK它返回一个 token你拿这个 token 去调 Firestore。Supabase Auth 则完全不同它本质上是一个高度封装的 Postgres 扩展。当你调用supabase.auth.signUp()它做的第一件事就是在auth.users表里插入一条记录并生成一个加密的 JWT。这个 JWT 的 payload 里sub字段直接就是auth.users.id。所以当你后续调用supabase.from(profiles).select()时Supabase 客户端会自动把 JWT 放进Authorization: Bearer xxx头里后端的 PostgREST 层Supabase 的核心 API 网关会解析这个 JWT拿到sub然后把它作为auth.uid()函数的返回值喂给数据库的 RLS 策略。整个链路没有中间状态没有额外的 token 验证服务没有网络跳转。这就是为什么它的实时订阅Realtime能如此轻量——变更事件直接从数据库的 WALWrite-Ahead Log日志里捕获通过逻辑复制Logical Replication推送到客户端延迟通常在 100ms 以内。我实测过在杭州阿里云 ECS 上部署的 Supabase连接北京的 Vue 前端从点击“提交订单”到另一个浏览器窗口看到新订单平均耗时 83ms。第三“无服务器”Serverless的实现方式是“去服务器化”而非“函数即服务”。很多开发者一听到“无服务器”就想到 AWS Lambda 或 Vercel Functions。Supabase 的“无服务器”是指你不需要为每一个业务接口如/api/orders/create去写、部署、运维一个独立的函数。它的 API 是完全声明式的你定义好数据库表结构、写好 RLS 策略、配置好 Storage 的 bucket 权限API 就自动存在了。POST /rest/v1/orders就是创建订单GET /rest/v1/orders?select*,user(*)就是关联查询。这种设计牺牲了一定的灵活性比如你不能在创建订单时同时发一封邮件并调用第三方物流 API但它换来了惊人的开发效率和极低的运维复杂度。对于 80% 的 CRUD 类应用这恰恰是最优解。我自己维护的 7 个项目里有 5 个是纯数据驱动的管理后台它们的后端代码行数加起来不到 200 行全是 Supabase 客户端调用没有一行 Node.js 后端逻辑。2.2 为什么选择自建而不是直接用 Supabase 官方托管服务官方托管supabase.com确实开箱即用点几下鼠标就能创建项目。但在我经手的项目中有 4 个最终选择了自建。原因非常具体且都来自真实客户的合同条款或内部审计要求数据驻留Data Residency一个为长三角某市卫健委开发的基层医生随访系统合同白纸黑字写着“所有患者数据不得离开华东二区上海”。Supabase 官方托管的免费层节点在美国付费层虽然支持区域选择但价格是自建的 3 倍以上且无法保证物理服务器的绝对位置。而我们用一台 4C8G 的阿里云 ECS装上 Docker15 分钟就完成了部署数据完全留在客户指定的机房。定制化审计日志医院信息系统对操作日志有严格要求必须记录“谁、在什么时间、对哪条记录、做了什么操作INSERT/UPDATE/DELETE、前后数据快照”。Supabase 官方托管只提供基础的请求日志IP、URL、状态码不包含数据库行级变更详情。而自建时我们可以直接在 Postgres 里启用pg_audit扩展或者用wal2json插件把 WAL 日志解析成 JSON 流推送到 Kafka 或 Elasticsearch实现毫秒级、全字段的审计追踪。这在官方托管服务里是不可配置的。网络策略与防火墙一个为军工研究所做的设备状态监控看板其内网有严格的出网白名单。Supabase 官方托管的域名*.supabase.co不在白名单内且不允许添加自定义域名或 IP 白名单。而自建时我们可以把 Supabase 的所有服务Auth、API、Realtime、Storage都部署在一个内网地址如supabase.internal前端通过内网 DNS 解析完全不经过公网彻底规避了网络策略问题。这三点没有一个是技术炫技全是甲方爸爸在招标文件里用加粗字体写出来的硬性指标。Supabase 官方托管是“开箱即用”而自建是“按需裁剪”。前者适合 MVP 快速验证后者才是生产环境的基石。2.3 Supabase 与 Firebase 的核心能力对比不是功能列表而是工作流映射网上很多对比文章罗列“Supabase 有 A、B、C 功能Firebase 有 X、Y、Z 功能”这毫无意义。真正的对比应该映射到开发者每天的真实工作流上。下面是我整理的、基于 7 个项目实战经验的“高频任务-解决方案”对照表开发者日常任务Firebase 方案Supabase 方案我的实操体会新用户注册并自动创建个人资料1. 调createUserWithEmailAndPassword()2. 监听onAuthStateChanged3. 在监听回调里手动调setDoc(db.collection(users).doc(uid), {...})创建 profile 文档1. 调supabase.auth.signUp({ email, password })2.自动触发auth.users表插入3. 配置auth.users表的on_insert数据库函数自动向public.profiles表插入对应记录Firebase 需要 3 步且第 3 步是应用层逻辑容易失败网络抖动、Promise 未 catch。Supabase 用数据库触发器Trigger实现原子性保障失败即回滚无需前端重试。我曾因 Firebase 的第 3 步失败导致 12 个用户只有 Auth 记录没有 Profile不得不写脚本人工修复。限制用户只能查看自己创建的数据1. 在 Firestore 安全规则里写match /posts/{postId} { allow read: if request.auth ! null resource.data.userId request.auth.uid; }2. 在应用层每次查询都必须加where(userId, , currentUser.uid)1. 在public.posts表上启用 RLS2. 写策略USING (auth.uid() user_id)3.前端直接调supabase.from(posts).select()无需加任何 where 条件Firebase 规则和应用层查询是两套逻辑极易不一致。我见过太多团队因为忘了在某个新写的查询里加where导致数据泄露。Supabase 的 RLS 是数据库强制的前端怎么查都安全。实现“点赞”功能且防止重复点赞1. 在 Firestore 里用transaction更新一个likesCount字段2. 同时在user_likes子集合里写入一条记录用set(..., { merge: true })1. 在public.likes表里设置(user_id, post_id)为复合主键或唯一索引2. 前端调supabase.from(likes).insert({ user_id, post_id })3. 捕获409 Conflict错误提示“已点赞”Firebase 的事务是跨文档的性能差且在高并发下可能失败。Supabase 直接用数据库的唯一约束这是最高效、最可靠的防重手段。我压测过单台 4C8G 服务器每秒能处理 1200 次点赞插入错误率 0%。文件上传到云存储并关联到用户1. 调ref(storage, images/ uid / fileName).put(file)2. 获取下载 URL3. 手动更新users文档存入该 URL1. 配置 Storage bucket 的 RLS允许auth.uid() bucket.owner_id2. 前端调supabase.storage.from(avatars).upload( uid / fileName, file)3.自动返回一个带签名的、有时效的 URLFirebase 的下载 URL 是公开的一旦泄露任何人都能下载。Supabase 的 URL 是带签名的类似https://.../avatar.jpg?tokenxxxexpires1234567890过期即失效安全性更高。这张表的核心结论是Firebase 的安全模型是“应用层防御”Supabase 的安全模型是“数据库层防御”。前者依赖开发者的自律和代码质量后者依赖数据库引擎的强制力。在小团队、快速迭代的场景下后者带来的确定性远胜于前者带来的那一点点灵活性。3. 自建 Supabase 全流程实操从零开始一步不跳过3.1 环境准备与基础依赖安装自建 Supabase 的本质是部署一个由多个 Docker 容器组成的微服务集群。官方推荐使用supabase/cli工具但我在生产环境中始终坚持手动编写docker-compose.yml。原因很简单CLI 工具会隐藏很多细节一旦出问题你连日志都不知道该去哪个容器里查。而手动编排每一个端口、每一个卷、每一个环境变量都清清楚楚。下面是我目前在所有项目中通用的、经过 7 次线上验证的最小可行配置。首先确认你的服务器满足最低要求。这不是官方文档里写的“2C4G”而是我踩坑后的血泪总结CPU至少 2 核。Auth 服务GoTrue和 Realtime 服务Elixir都是 CPU 密集型单核会导致 JWT 签发和 WebSocket 连接建立明显延迟。内存绝对不能低于 4GB。Postgres 本身就需要 1.5GB 以上加上其他服务2GB 内存的机器在启动后 5 分钟内就会被 OOM Killer 干掉。我第一次部署就栽在这里日志里全是Killed process 1234 (postgres) total-vm:4567890kB, anon-rss:123456kB, file-rss:0kB。磁盘SSD 是必须的。HDD 在高并发 WAL 写入时IOPS 会成为瓶颈Realtime 订阅延迟飙升到秒级。我用过一块 1TB 的 SATA SSD表现完美。操作系统我强烈推荐 Ubuntu 22.04 LTS。Debian 11 也可以但 Ubuntu 的内核更新更及时对 Docker 的兼容性更好。CentOS Stream 或 Rocky Linux 请绕道它们的 SELinux 策略和 Docker 的 cgroup v2 有兼容性问题会浪费你至少 8 小时排查时间。安装 Docker 和 Docker Compose# 卸载旧版本如果存在 sudo apt-get remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt-get update sudo apt-get install -y \ ca-certificates \ curl \ gnupg \ lsb-release # 添加 Docker 官方 GPG 密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 设置稳定版仓库 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装 Docker Engine sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 验证 sudo docker run hello-world注意docker-compose-plugin是新版 Docker Composev2.x它不再是独立的docker-compose二进制而是docker compose子命令。务必确认你的docker compose version输出是Docker Compose version v2.x.x而不是docker-compose version 1.x.x。旧版在 Supabase 的复杂网络配置下会出现容器间 DNS 解析失败的问题。3.2 核心配置文件docker-compose.yml详解这是整个自建过程的“心脏”。我不会给你一个黑盒的、一键复制粘贴的文件而是逐行解释每一部分的作用、参数含义以及我为什么这样配置。你可以直接复制但更重要的是理解它。version: 3.8 # 定义网络让所有服务在同一个内网中通信 networks: supabase: driver: bridge # 关键必须设置为 IPv4否则 Realtime 服务无法正确识别客户端 IP ipam: config: - subnet: 172.28.0.0/16 # 定义所有需要挂载的卷用于持久化数据 volumes: # Postgres 数据目录绝对不能丢 db_data: driver: local # pgAdmin 的配置和会话数据 pgadmin_data: driver: local # Supabase StudioWeb UI的配置 studio_data: driver: local # 对象存储Storage的文件数据 storage_data: driver: local services: # 1. Postgres 数据库主服务 db: image: supabase/postgres:15.3.0.141 restart: always # 关键必须设置为 host.docker.internal否则其他容器无法通过 localhost 访问它 # 这是 Docker Desktop 的特性在 Linux 服务器上需要用 network_mode: host 或额外配置 extra_hosts: - host.docker.internal:host-gateway environment: # 数据库超级用户密码这是整个集群的 root 密码请务必修改 POSTGRES_PASSWORD: your-super-secret-password-here # Supabase 的内部连接字符串其他服务会用这个连接它 POSTGRES_HOST: db POSTGRES_USER: postgres POSTGRES_DB: postgres # 启用 pgAudit用于审计日志 POSTGRES_AUDIT: on # 设置时区避免时间戳混乱 TZ: Asia/Shanghai volumes: - db_data:/var/lib/postgresql/data networks: - supabase # 关键端口映射5432 是标准 Postgres 端口供外部客户端如 DBeaver连接 ports: - 5432:5432 # 健康检查Docker 会定期检查数据库是否响应 healthcheck: test: [CMD-SHELL, pg_isready -U postgres -d postgres] interval: 30s timeout: 10s retries: 5 # 2. Supabase Auth 服务GoTrue auth: image: supabase/gotrue:v2.122.0 restart: always environment: # JWT 签名密钥必须与 API 服务保持一致这是安全的关键 GOTRUE_JWT_SECRET: your-jwt-secret-key-here-32-chars-minimum # JWT 过期时间单位是秒。我设为 36001小时平衡安全与用户体验 GOTRUE_JWT_EXPIRY: 3600 # 数据库连接信息指向上面的 db 服务 DATABASE_URL: postgresql://postgres:your-super-secret-password-heredb:5432/postgres # 允许的跨域来源开发时可以设为 *生产环境必须精确到域名 GOTRUE_SITE_URL: http://localhost:3000 GOTRUE_ADDITIONAL_REDIRECT_ALLOWED_URLS: http://localhost:3000 # SMTP 邮件配置用于邮箱验证和密码重置 GOTRUE_SMTP_HOST: smtp.gmail.com GOTRUE_SMTP_PORT: 587 GOTRUE_SMTP_USER: your-emailgmail.com GOTRUE_SMTP_PASS: your-app-password-here GOTRUE_SMTP_ADMIN_EMAIL: your-emailgmail.com # 时区 TZ: Asia/Shanghai depends_on: db: condition: service_healthy networks: - supabase # 3. Supabase API 服务PostgREST api: image: supabase/postgrest:v12.0.1 restart: always environment: # PostgREST 连接数据库的 URL PGRST_DB_URI: postgresql://postgres:your-super-secret-password-heredb:5432/postgres # JWT 密钥必须与 Auth 服务完全一致 PGRST_JWT_SECRET: your-jwt-secret-key-here-32-chars-minimum # 启用动态 schema允许前端查询 public schema 下的所有表 PGRST_DB_SCHEMA: public # 启用 RLS这是 Supabase 安全的基石 PGRST_DB_USE_LEGACY_GUCS: false # 时区 TZ: Asia/Shanghai depends_on: db: condition: service_healthy networks: - supabase # 4. Supabase Realtime 服务Elixir realtime: image: supabase/realtime:v2.10.1 restart: always environment: # Realtime 服务的 JWT 密钥同样必须一致 REALTIME_JWT_SECRET: your-jwt-secret-key-here-32-chars-minimum # 数据库连接 REALTIME_PG_URL: postgresql://postgres:your-super-secret-password-heredb:5432/postgres # Realtime 服务监听的端口 PORT: 4000 # 时区 TZ: Asia/Shanghai depends_on: db: condition: service_healthy networks: - supabase # Realtime 使用 WebSocket需要暴露端口 ports: - 4000:4000 # 5. Supabase Storage 服务Elixir storage: image: supabase/storage-api:v1.10.6 restart: always environment: # Storage 的 JWT 密钥还是那个密钥 STORAGE_JWT_SECRET: your-jwt-secret-key-here-32-chars-minimum # 数据库连接 STORAGE_POSTGRES_URL: postgresql://postgres:your-super-secret-password-heredb:5432/postgres # 对象存储的根路径 STORAGE_S3_BUCKET: supabase-storage # S3 兼容的 endpoint这里我们用本地 MinIO但 Supabase Storage 也支持直接对接 AWS S3 STORAGE_S3_ENDPOINT: http://minio:9000 # 时区 TZ: Asia/Shanghai depends_on: db: condition: service_healthy minio: condition: service_started networks: - supabase # 6. MinIO 对象存储Supabase Storage 的后端 minio: image: minio/minio:RELEASE.2023-09-12T09-58-54Z restart: always environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin # 启用控制台 MINIO_CONSOLE_ADDRESS: :9001 volumes: - storage_data:/data networks: - supabase # MinIO 的 API 端口和控制台端口 ports: - 9000:9000 - 9001:9001 # 7. Supabase StudioWeb UI 管理界面 studio: image: supabase/studio:2023.10.25-8e4a5c9 restart: always environment: # Studio 连接 API 服务的地址 SUPABASE_URL: http://api:3000 # Studio 连接 Auth 服务的地址 SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 SUPABASE_SERVICE_ROLE_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.S3NR9LqP1Wt1uQZzQzQzQzQzQzQzQzQzQzQzQzQzQzQ # 时区 TZ: Asia/Shanghai depends_on: api: condition: service_started auth: condition: service_started networks: - supabase # Studio 的 Web 界面端口 ports: - 3000:3000 # 8. pgAdminPostgres 图形化管理工具 pgadmin: image: dpage/pgadmin4:7.10 restart: always environment: PGADMIN_DEFAULT_EMAIL: adminadmin.com PGADMIN_DEFAULT_PASSWORD: admin # pgAdmin 连接数据库的配置 PGADMIN_SERVER_JSON_FILE: /pgadmin/servers.json volumes: - pgadmin_data:/var/lib/pgadmin - ./pgadmin-servers.json:/pgadmin/servers.json networks: - supabase # pgAdmin 的 Web 界面端口 ports: - 5050:80提示your-super-secret-password-here和your-jwt-secret-key-here-32-chars-minimum是两个必须修改的占位符。JWT 密钥必须是 32 个字符以上的随机字符串可以用openssl rand -base64 32生成。数据库密码也要足够强壮。这两个密钥一旦泄露整个 Supabase 集群的安全性就荡然无存。3.3 启动集群与首次初始化配置文件写好后保存为docker-compose.yml然后执行# 在 docker-compose.yml 所在目录下执行 sudo docker compose up -d这条命令会以后台模式-d启动所有服务。docker compose up会自动拉取镜像如果本地没有的话创建网络创建卷然后依次启动容器。整个过程大约需要 2-3 分钟。启动后用sudo docker compose ps查看所有服务的状态。你应该看到所有服务的STATUS都是running。如果某个服务是exited或unhealthy那就需要看日志了# 查看某个服务的日志例如数据库 sudo docker compose logs db # 查看实时日志按 CtrlC 退出 sudo docker compose logs -f api最常见的启动失败原因是数据库健康检查失败。日志里会显示pg_isready: could not connect to server: Connection refused。这通常是因为db服务启动太慢而auth或api服务已经急着去连接它了。解决方案是在docker-compose.yml中为auth和api服务添加restart: on-failure并增加一个简单的等待脚本但这会增加复杂度。我的做法是先单独启动db服务sudo docker compose up -d db # 等待 30 秒确认 db 启动成功 sudo docker compose logs db | tail -20 # 然后再启动其他服务 sudo docker compose up -d auth api realtime storage minio studio pgadmin集群启动成功后你就可以通过浏览器访问各个服务了Supabase Studio:http://your-server-ip:3000—— 这是你的 Web 管理后台可以创建项目、管理表、写 RLS 策略。pgAdmin:http://your-server-ip:5050—— 输入adminadmin.com/admin登录然后添加一个服务器主机名填db端口5432用户名postgres密码就是你配置的POSTGRES_PASSWORD。MinIO Console:http://your-server-ip:9001—— 输入minioadmin/minioadmin登录可以管理对象存储的 bucket。注意如果你的服务器有防火墙如 ufw请确保开放了3000,4000,5050,5432,9000,9001这些端口。ufw allow 3000等命令即可。3.4 创建第一个项目与数据库表打开http://your-server-ip:3000你会看到一个简洁的登录页面。首次访问Studio 会引导你创建一个新项目。点击 “Create new project”填写项目名称如my-first-app然后点击 “Create”。创建完成后你会进入项目的仪表盘。左侧导航栏里点击 “Table Editor”。这是你操作数据库的起点。点击右上角的 “New table” 按钮。在弹出的对话框中Name:usersSchema:publicColumns: 添加以下几列id(UUID, Primary Key, Default:gen_random_uuid())email(Text, Unique)created_at(Timestamp with time zone, Default:now())点击 “Create table”。现在users表已经创建好了。但此时它只是一个空壳。为了让它能被 Supabase 的 Auth 服务使用你需要将它与auth.users表关联起来。Supabase 的 Auth 服务在用户注册时会自动在auth.users表里创建一条记录。我们需要一个数据库函数在auth.users表插入新记录时自动在public.users表里创建对应的记录。在 Studio 的左侧导航栏点击 “SQL Editor”。粘贴并执行以下 SQL-- 创建一个函数用于在 auth.users 插入时自动创建 public.users 记录 CREATE OR REPLACE FUNCTION public.handle_new_user() RETURNS TRIGGER AS $$ BEGIN INSERT INTO public.users (id, email, created_at) VALUES (NEW.id, NEW.email, NOW()); RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; -- 创建一个触发器绑定到 auth.users 表 CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user();这段 SQL 的作用是每当auth.users表里新增一条用户记录就自动触发handle_new_user()函数向public.users表里插入一条对应的数据。这样用户注册后你就不需要在前端再发一次请求来创建 profile 了。接下来为public.users表启用 RLSRow Level Security并写一条策略让每个用户只能读取自己的记录在 Studio 的 Table Editor 里找到users表点击右侧的 “RLS Policies” 标签页。点击 “Add policy”填写Policy name:users_select_ownOperation:SELECTUsing expression:id auth.uid()点击 “Save”。这条策略的意思是“当执行 SELECT 操作时只允许id字段等于当前认证用户 ID 的行被返回”。现在你的第一个 Supabase 项目就准备好了。你可以用任何支持 HTTP 的工具如 curl、Postman来测试它。3.5 前端接入以 Vue 3 为例的完整代码示例Supabase 的前端 SDK 是其体验的灵魂。它把复杂的数据库操作封装成了极其简洁的链式调用。下面是一个完整的 Vue 3 组件演示了如何实现用户注册、登录、读取自己的用户信息。首先安装 SDKnpm install supabase/supabase-js然后在你的项目中创建一个supabaseClient.js文件import { createClient } from supabase/supabase-js // 这里的 URL 和 ANON_KEY需要从你的 Supabase Studio 里获取 // 在 Studio 的左侧导航栏点击 “Project Settings” - “API”就能看到 const supabaseUrl http://your-server-ip:3000 const supabaseAnonKey eyJhbGciOiJIUzI1NiIsInR5c

相关新闻