2026年,后端开发的门槛正在被开源BaaS(后端即服务)平台大幅拉低。你不需要再为配置服务器、搭建数据库或管理用户认证而熬夜,只需几行代码,就能获得数据库、实时API和云存储,但这把“利器”用得不好,随时可能让你的数据裸奔。以下6个实战技巧,能帮你避开那些最常见的坑。
分清客户端与服务器权限
一个典型的致命错误是将高权限的密钥直接放在前端代码里,这等于把数据库密码贴在电脑屏幕上。BaaS平台通常会提供两种密钥:一个是给浏览器用的匿名密钥(anon key),另一个是拥有完整管理权限的私有密钥(service_role key)。
任何需要在客户端运行的代码,必须使用匿名密钥。私有密钥必须严格保存在你的后端服务器或云函数环境中,绝不能出现在前端打包文件中。一旦泄露,攻击者可以利用它绕过所有安全规则,清空你的整个数据库。
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
集成ORM实现类型安全
import { createClient } from '@supabase/supabase-js';
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
手动拼接SQL字符串既容易出错又难以维护。通过集成像Drizzle这样的ORM(对象关系映射)工具,你可以用TypeScript写出类型安全的查询。这意味着你的代码编辑器能自动补全表名和字段,并在编译阶段就捕获语法错误。
你只需要获取BaaS提供的数据库连接字符串,在项目中配置好Drizzle,然后定义你的数据模式。这样,你既能完全掌控数据库的迁移和查询,又能享受静态类型带来的开发效率提升,而且这些类型检查在最终运行时没有任何额外开销。
强制开启行级安全策略
import postgres from 'postgres';
import { drizzle } from 'drizzle-orm/postgres-js';
const client = postgres(process.env.SUPABASE_DB_URL); // 例如:'postgres://user:password@host:5432/db'
export const db = drizzle(client);
很多人以为数据库放在云端,别人就访问不了,这是大错特错的。你必须启用行级安全(RLS)策略,它相当于为你的每一张表都装上了“指纹锁”。不开启RLS,任何知道API地址的人都能遍历你的所有数据。
import { pgTable, serial, varchar, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull(),
createdAt: timestamp('created_at').defaultNow()
});
开启RLS后,你需要为每个操作(如SELECT、INSERT)创建策略。例如,一条典型的策略是“仅允许用户查看user_id等于当前认证用户ID的记录”。这依赖于内置的auth.uid()函数来识别当前登录者。配置好后,你就可以放心地在前端直接查询数据,而不必担心用户越权。
用边缘函数处理后台任务
有时候你需要执行一些不能暴露给客户端的敏感逻辑,比如支付回调验证、生成水印或发送邮件。边缘函数允许你在离用户最近的服务器节点上运行Deno代码,既保证了安全性,又降低了延迟。
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
比如当用户注册后,你可以编写一个边缘函数,监听数据库的INSERT事件,自动触发一封包含自定义参数的欢迎邮件。部署这类函数通常只需一行命令,它们与认证系统无缝集成,可以直接使用环境变量中的密钥,无需额外配置。
CREATE POLICY "Users can access their profile"
ON profiles
FOR SELECT USING (auth.uid() = id);
存储文件签名URL与权限控制
直接把用户上传的头像设为公开可读,会带来隐私风险和“流量盗刷”。BaaS的存储模块基于S3协议构建,但通过RLS策略,你可以像控制数据库行一样控制文件的访问权限。
正确的做法是,上传文件时设置桶为私有。当需要让用户查看图片时,通过后端生成一个带有过期时间的签名URL。你可以编写一条策略,规定只有文件的拥有者或特定角色的用户才能触发这个签名URL的生成,从而精确控制每一个字节的流向。
关联自定义表与认证用户
BaaS帮你处理了注册登录,并生成了唯一的用户UUID。但你肯定需要存储用户的昵称、头像或个人设置等额外信息。这时,你需要创建一张自定义的“用户资料表”,并将它的主键设置为与认证系统的用户ID相关联。
// sendWelcomeEmail.ts
import { serve } from "https://deno.land/std/http/server.ts";
serve(async (req) => {
const { email } = await req.json();
// 在这里使用 Resend 或 Mailgun 等提供商发送电子邮件逻辑
return new Response(JSON.stringify({ status: "sent" }), { status: 200 });
});
在数据库中,你需要声明这个字段为外键,指向auth.users表。这样,当你在应用里查询某条记录时,就可以通过JOIN操作,直接从你的业务表中获取用户的详细信息,同时保证了数据的参照完整性,不会出现“幽灵”用户数据。
supabase functions deploy sendWelcomeEmail
你在使用这类BaaS工具时,踩过最大的坑是什么?欢迎在评论区分享你的经历,点赞让更多开发者看到这些避坑指南!

