Supabase 入门:PostgreSQL + Auth + Storage 一站式后端
凌晨三点,盯着屏幕上第 12 次报错的数据库连接错误,我意识到一件事:前端开发者想做个全栈项目,真的太难了。
说实话,以前每次需要后端功能,我都得去学 Node.js、Express、还要搞定数据库配置、用户认证、文件存储——每个都是个大坑。直到我遇见了 Supabase。
这玩意儿说白了就是个开源的 Firebase 替代品,但它用的是 PostgreSQL,不是那种让你头疼的 NoSQL。而且它把数据库、用户认证、文件存储这三件事打包在一起了——嗯,一站式后端服务。
这篇文章会带你从头开始用 Supabase,重点讲三大核心功能:Database、Auth、Storage。读完之后,你大概就能用它快速搭建一个完整的后端,不用再被那些配置文件折磨了。
什么是 Supabase
先聊聊 Supabase 到底是个啥。
它是个 BaaS 平台,Backend as a Service——后端即服务。啥意思呢?就是你不用自己搭服务器、配置数据库、写 API,它把这些都帮你搞定了。
跟 Firebase 不一样,Supabase 是完全开源的。而且它用的是 PostgreSQL,这个很重要。PostgreSQL 是关系型数据库,能做复杂的 SQL 查询,数据关系也很清晰——比 Firestore那种文档型数据库好用多了。
Supabase 有六大核心功能:
- Database:PostgreSQL 数据库,支持 SQL 查询,还自动生成 REST API
- Auth:用户认证系统,支持邮箱登录、社交登录(Google、GitHub等),还有 JWT Token
- Storage:文件存储,类似 AWS S3,但更简单
- Realtime:实时数据同步,适合做聊天应用
- Edge Functions:边缘计算,类似 AWS Lambda
- Vector Database:向量数据库,适合做 AI 应用
不过这篇文章我们只讲前三样:Database、Auth、Storage——这三个是最核心的,其他功能以后再说。
说到这,你可能会想:开源的、用 PostgreSQL、还有认证和存储——那它跟 Firebase 比到底谁更好?嗯,这个后面会详细聊,先说结论:如果你需要 SQL 查询、数据关系复杂、或者想自己掌控数据,选 Supabase;如果你做移动应用、需要实时同步功能特别强,选 Firebase。
快速开始
创建 Supabase 项目
第一步,去 supabase.com 注册个账号。注册完之后,点”New Project”创建新项目。
创建项目的时候需要填几个东西:
- 项目名称:随便起,比如
my-first-app - 数据库密码:这个要记下来,后面会用到
- 区域:选离你近的,国内的话选 Singapore 或 Tokyo
大概三分钟就能创建好。创建完之后,你会看到项目面板,里面有个 Dashboard,左边是一堆功能菜单:Table Editor、Authentication、Storage、Edge Functions……看着挺多的,不过别慌,这篇文章会一个个讲。
安装和初始化
现在需要在你的前端项目里装 Supabase。先装依赖:
npm install @supabase/supabase-js
装好之后,去 Supabase Dashboard 找两个东西:Project URL 和 Anon Public Key。这两个在 Settings > API 里能找到。
然后初始化 Supabase Client:
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = 'https://your-project-id.supabase.co';
const supabaseAnonKey = 'your-anon-public-key';
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
这段代码就是你跟 Supabase 通信的入口。后面所有操作——数据库查询、用户登录、文件上传——都得通过这个 supabase 对象。
连接测试
初始化完之后,先测试一下能不能连上。最简单的方法是查一下数据库里有没有数据:
const { data, error } = await supabase.from('users').select('*');
if (error) {
console.error('连接失败:', error.message);
} else {
console.log('连接成功!', data);
}
如果报错说找不到 users 表,那是正常的——你还没创建表呢。下一节会教你怎么创建表。
常见的错误有几种:
- URL 或 Key 拼错了:检查一下是不是完整复制了
- CORS 错误:前端项目可能需要配置 CORS,不过 Supabase 默认是允许的
- 网络问题:偶尔会遇到,等几分钟再试
如果一切正常,你就能看到返回的数据了——可能是空数组 [],说明表里还没数据。
Database:数据库操作
这块是 Supabase 的核心。如果你以前用过 PostgreSQL,会觉得挺熟悉的;如果你是前端开发者,可能不太习惯 SQL,不过 Supabase 提供了可视化的 Table Editor,不用写 SQL 也能操作数据库。
创建数据表
有两种方式创建表:用 Table Editor(可视化),或者写 SQL。
先说 Table Editor。在 Dashboard 左边点”Table Editor”,然后点”Create a new table”。你需要填表名、字段名、字段类型。
举个例子,创建一个 users 表:
| 字段名 | 类型 | 约束 |
|---|---|---|
| id | int8 | Primary Key, Auto Increment |
| text | Unique, Not Null | |
| name | text | - |
| created_at | timestamptz | Default: now() |
填完之后点”Save”,表就创建好了。
不过我觉得写 SQL 更快,特别是你以后要做复杂操作的时候。Supabase 提供了 SQL Editor,在 Dashboard 左边能找到。
创建 users 表和 projects 表的 SQL:
-- 用户表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 项目表
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
title TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'active',
created_at TIMESTAMPTZ DEFAULT NOW()
);
这里有个细节:projects 表里的 user_id 是外键,关联到 users 表的 id。这样一来,每个项目都属于某个用户——这就是关系型数据库的好处,数据关系很清晰。
数据操作 CRUD
现在表创建好了,来试试增删改查。
插入数据:
// 插入一个用户
const { data, error } = await supabase
.from('users')
.insert([
{ email: '[email protected]', name: 'John Doe' }
]);
if (error) {
console.error('插入失败:', error.message);
} else {
console.log('插入成功:', data);
}
查询数据:
// 查询所有项目,顺便把用户信息也带上
const { data, error } = await supabase
.from('projects')
.select(`
*,
users (
name,
email
)
`)
.eq('status', 'active')
.order('created_at', { ascending: false });
console.log(data);
这个查询有点意思:它不仅查了 projects 表,还通过外键把关联的用户信息带出来了——一句查询就拿到了两张表的数据。Firestore 要做到这点得写好几轮查询,还得手动拼数据。
更新数据:
const { data, error } = await supabase
.from('projects')
.update({ status: 'completed' })
.eq('id', 1);
删除数据:
const { data, error } = await supabase
.from('projects')
.delete()
.eq('id', 1);
嗯,这些操作都很直观。不过有个细节:.eq() 是条件过滤,意思是”等于”。Supabase 还提供很多其他过滤方法,比如 .gt()(大于)、.lt()(小于)、.like()(模糊匹配)——基本上 SQL 的常用过滤都支持。
Row Level Security (RLS)
这块挺重要,特别是你做用户相关功能的时候。
RLS 全称是 Row Level Security,行级安全。啥意思呢?就是你可以控制用户只能看到自己的数据,不能看别人的。
举个例子:projects 表里有很多项目,但你希望用户登录之后只能看到自己创建的项目,不能看到别人的。
先启用 RLS:
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
然后创建 Policy:
-- 用户只能看到自己的项目
CREATE POLICY "Users can view their own projects"
ON projects FOR SELECT
USING (user_id = auth.uid());
这里 auth.uid() 是 Supabase 提供的函数,返回当前登录用户的 ID。这个 Policy 的意思是:查询 projects 表的时候,只返回 user_id 等于当前用户 ID 的记录。
同理,你还能创建插入、更新、删除的 Policy:
-- 用户只能插入自己的项目
CREATE POLICY "Users can insert their own projects"
ON projects FOR INSERT
WITH CHECK (user_id = auth.uid());
-- 用户只能更新自己的项目
CREATE POLICY "Users can update their own projects"
ON projects FOR UPDATE
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());
-- 用户只能删除自己的项目
CREATE POLICY "Users can delete their own projects"
ON projects FOR DELETE
USING (user_id = auth.uid());
这样一来,就算有人想恶意查询别人的数据,也查不到——数据库层面就拦截了。比在前端代码里判断安全多了。
Auth:用户认证系统
认证这块,Supabase 做得挺完整的。支持很多登录方式:邮箱密码登录、无密码登录(Magic Link)、一次性密码(OTP)、社交登录(Google、GitHub、Apple 等 20 多个平台)、手机号登录、还有企业级 SSO。
认证功能概述
先说说 Supabase Auth 的核心机制。
它用的是 JWT Token——JSON Web Token。用户登录成功之后,Supabase 会生成一个 Token,前端拿着这个 Token 去请求后端 API。后端验证 Token,确认用户身份。
而且 Supabase Auth 会自动把用户信息存到 PostgreSQL 的 auth.users 表里——这个表是自动创建的,你不用管。用户 ID、邮箱、创建时间、最后登录时间……这些信息都在里面。
还有个 Session Persistence,会话持久化。啥意思呢?就是用户登录之后,浏览器会记住这个登录状态,下次打开页面不用重新登录。Supabase 的前端 SDK 自动处理这个,你不用写额外的代码。
Email/Password 认证
这是最常见的登录方式。先说注册:
// 用户注册
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'securepassword123',
});
if (error) {
console.error('注册失败:', error.message);
} else {
console.log('注册成功:', data);
}
注册成功之后,Supabase 会发一封确认邮件到用户的邮箱。用户点邮件里的链接,账号就激活了。这个是默认行为,你可以在 Dashboard 里关掉,不过不建议——确认邮箱能防止恶意注册。
然后是登录:
// 用户登录
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'securepassword123',
});
if (error) {
console.error('登录失败:', error.message);
} else {
console.log('登录成功:', data);
}
登录成功之后,你就能拿到用户信息:
// 获取当前登录用户
const { data: { user } } = await supabase.auth.getUser();
console.log('当前用户:', user);
// 输出类似:{ id: 'abc123', email: '[email protected]', ... }
最后是登出:
await supabase.auth.signOut();
登出之后,Session 就清空了,用户得重新登录。
还有个常见需求:密码重置。Supabase 提供了现成的功能:
// 发送密码重置邮件
const { data, error } = await supabase.auth.resetPasswordForEmail(
'[email protected]'
);
用户收到邮件,点链接就能重置密码。整个过程你不用写额外的逻辑。
Social Auth 社交登录
这个功能挺受欢迎的——用户不用填表,直接用 Google 或 GitHub 账号登录,体验好很多。
先配置社交登录。在 Dashboard 里点 Authentication > Providers,然后启用你想要的平台,比如 Google 或 GitHub。
每个平台的配置不一样,不过大致步骤类似:
- 在 Google/GitHub 创建 OAuth App
- 复制 Client ID 和 Client Secret 到 Supabase
- 配置回调 URL(Redirect URL)
配置完之后,前端代码很简单:
// Google 登录
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
});
// GitHub 登录
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
});
调用之后,页面会跳转到 Google/GitHub 的授权页面,用户点”同意”,再跳回你的应用。这时候用户就登录成功了。
你还能自定义回调 URL 和额外参数:
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'https://your-app.com/dashboard',
queryParams: {
access_type: 'offline',
prompt: 'consent',
}
}
});
redirectTo 是登录成功之后跳转的页面;queryParams 是额外的 OAuth 参数,比如要求离线访问、每次都询问授权。
RLS 结合 Auth
前面讲 RLS 的时候提到了 auth.uid(),这块现在能串起来了。
用户登录之后,auth.uid() 就返回用户的 ID。你在数据库 Policy 里用这个 ID,就能实现”用户只能访问自己的数据”。
举个例子:用户登录之后创建项目,希望项目自动关联到当前用户。
// 创建项目,自动关联当前用户
const { data: { user } } = await supabase.auth.getUser();
const { data, error } = await supabase
.from('projects')
.insert([
{
title: 'New Project',
user_id: user.id // 从 Auth 获取用户 ID
}
]);
然后 RLS Policy 确保:
- 查询的时候,只能看到
user_id = auth.uid()的项目 - 插入的时候,
user_id必须等于auth.uid() - 更新和删除同理
这样一来,认证和权限控制就完整了——用户登录、数据关联、权限限制,一条线打通。
Storage:文件存储
Supabase Storage 是对象存储服务,类似 AWS S3,但用起来简单很多。适合存用户头像、图片、文档这类文件。
Storage 功能概述
核心概念有两个:Bucket 和 Object。
- Bucket:存储桶,类似文件夹的概念。你可以创建多个 Bucket,比如
avatars存头像,documents存文档 - Object:具体文件,比如
avatar.jpg、report.pdf
Bucket 有两种权限模式:
- Public:公开桶,任何人都能访问文件(适合公开图片)
- Private:私有桶,只有授权用户能访问(适合私密文档)
创建 Bucket
在 Dashboard 点 Storage,然后点”New Bucket”创建桶。
举个例子,创建一个 avatars 桶存用户头像:
- 名称:
avatars - Public bucket:勾选(头像一般是公开的)
- File size limit:设置最大文件大小,比如 2MB
创建完之后,你就能往这个桶里上传文件了。
文件上传和下载
上传文件:
// 上传用户头像
const file = document.getElementById('avatar-input').files[0];
const { data, error } = await supabase.storage
.from('avatars')
.upload('user-id/avatar.jpg', file, {
cacheControl: '3600',
upsert: false
});
if (error) {
console.error('上传失败:', error.message);
} else {
console.log('上传成功:', data);
}
这里几个细节:
upload()第一个参数是文件路径:'user-id/avatar.jpg'cacheControl: 缓存时间,3600 秒upsert: 如果文件已存在,是否覆盖。false表示不覆盖,会报错;true表示覆盖
下载文件:
// 下载文件
const { data, error } = await supabase.storage
.from('avatars')
.download('user-id/avatar.jpg');
if (error) {
console.error('下载失败:', error.message);
} else {
// data 是 Blob 对象,可以转成 URL 显示
const url = URL.createObjectURL(data);
document.getElementById('avatar-img').src = url;
}
如果桶是公开的,你还能直接获取公开 URL:
// 获取公开 URL
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('user-id/avatar.jpg');
console.log('公开 URL:', data.publicUrl);
// 输出类似:https://your-project.supabase.co/storage/v1/object/public/avatars/user-id/avatar.jpg
私有桶的文件需要生成临时访问 URL:
// 生成临时访问 URL(有效期 1 小时)
const { data, error } = await supabase.storage
.from('documents')
.createSignedUrl('private-file.pdf', 3600);
console.log('临时 URL:', data.signedUrl);
删除文件:
const { data, error } = await supabase.storage
.from('avatars')
.remove(['user-id/avatar.jpg']);
访问控制
Storage 也支持 RLS Policy,控制用户能访问哪些文件。
举个例子:用户只能上传和访问自己文件夹里的文件。
-- 用户只能上传到自己的文件夹
CREATE POLICY 'Users can upload to their own folder'
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'avatars' AND
(storage.foldername(name))[1] = auth.uid()::text
);
-- 用户只能访问自己文件夹里的文件
CREATE POLICY 'Users can access their own files'
ON storage.objects FOR SELECT
USING (
bucket_id = 'avatars' AND
(storage.foldername(name))[1] = auth.uid()::text
);
这里 storage.foldername(name) 是提取文件路径的文件夹部分。[1] 取第一个文件夹名,也就是用户 ID。
这样一来,用户上传文件的时候,路径必须是 user-id/filename——RLS 会验证 user-id 是否等于当前用户 ID。不是的话就拒绝上传。
实战案例:任务管理应用
说了这么多,来个完整案例串一下。
假设我们要做一个简单的任务管理应用,功能包括:
- 用户注册和登录
- 创建任务、查看任务列表
- 上传任务附件
数据库设计
先设计表结构:
-- 用户表(Auth 自动创建,不用手动建)
-- tasks 表
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) NOT NULL,
title TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'completed')),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 启用 RLS
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;
-- Policy:用户只能访问自己的任务
CREATE POLICY 'Users can manage their own tasks'
ON tasks FOR ALL
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());
-- attachments 表(关联任务)
CREATE TABLE task_attachments (
id SERIAL PRIMARY KEY,
task_id INTEGER REFERENCES tasks(id) ON DELETE CASCADE,
file_name TEXT NOT NULL,
file_path TEXT NOT NULL,
file_size INTEGER,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 启用 RLS
ALTER TABLE task_attachments ENABLE ROW LEVEL SECURITY;
-- Policy:通过任务关联判断权限
CREATE POLICY 'Users can manage attachments of their tasks'
ON task_attachments FOR ALL
USING (
EXISTS (
SELECT 1 FROM tasks
WHERE tasks.id = task_attachments.task_id
AND tasks.user_id = auth.uid()
)
);
这里有个细节:task_attachments 表的 Policy 不是直接判断 user_id,而是通过 task_id 关联到 tasks 表,再判断 tasks.user_id。这样一来,只有任务的主人才能管理附件。
创建 Storage Bucket
创建一个 task-files 桶,设置为私有:
-- 在 Dashboard 创建,或者用 SQL
INSERT INTO storage.buckets (name, public)
VALUES ('task-files', false);
-- Policy:用户只能上传到自己的文件夹
CREATE POLICY 'Users can upload task files'
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'task-files' AND
(storage.foldername(name))[1] = auth.uid()::text
);
CREATE POLICY 'Users can access task files'
ON storage.objects FOR SELECT
USING (
bucket_id = 'task-files' AND
(storage.foldername(name))[1] = auth.uid()::text
);
核心代码实现
把所有功能串起来的代码:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
'https://your-project.supabase.co',
'your-anon-key'
);
// 1. 用户注册
async function register(email: string, password: string) {
const { data, error } = await supabase.auth.signUp({
email,
password,
});
if (error) throw error;
return data;
}
// 2. 用户登录
async function login(email: string, password: string) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) throw error;
return data;
}
// 3. 创建任务
async function createTask(title: string, description?: string) {
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('未登录');
const { data, error } = await supabase
.from('tasks')
.insert([
{
title,
description,
user_id: user.id,
}
])
.select();
if (error) throw error;
return data[0];
}
// 4. 获取任务列表
async function getTasks() {
const { data, error } = await supabase
.from('tasks')
.select('*')
.order('created_at', { ascending: false });
if (error) throw error;
return data;
}
// 5. 上传附件
async function uploadAttachment(taskId: number, file: File) {
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('未登录');
const filePath = user.id + '/' + taskId + '/' + file.name;
const { data, error } = await supabase.storage
.from('task-files')
.upload(filePath, file);
if (error) throw error;
// 记录到 task_attachments 表
const { data: attachment, error: dbError } = await supabase
.from('task_attachments')
.insert([
{
task_id: taskId,
file_name: file.name,
file_path: filePath,
file_size: file.size,
}
])
.select();
if (dbError) throw dbError;
return attachment[0];
}
// 6. 获取任务附件
async function getTaskAttachments(taskId: number) {
const { data, error } = await supabase
.from('task_attachments')
.select('*')
.eq('task_id', taskId);
if (error) throw error;
// 生成临时访问 URL
const attachmentsWithURLs = data.map(async (attachment) => {
const { data: urlData } = await supabase.storage
.from('task-files')
.createSignedUrl(attachment.file_path, 3600);
return {
...attachment,
url: urlData.signedUrl,
};
});
return Promise.all(attachmentsWithURLs);
}
// 7. 完成任务
async function completeTask(taskId: number) {
const { data, error } = await supabase
.from('tasks')
.update({ status: 'completed', updated_at: new Date() })
.eq('id', taskId)
.select();
if (error) throw error;
return data[0];
}
// 8. 登出
async function logout() {
await supabase.auth.signOut();
}
这套代码涵盖了用户注册登录、任务 CRUD、文件上传——基本上一个完整应用的骨架都有了。你可以在此基础上加更多功能,比如任务分类、标签、评论、通知……
Supabase vs Firebase 对比
说了这么多,你可能会想:Supabase 和 Firebase 到底选哪个?嗯,这块详细对比一下。
核心差异
| 对比维度 | Supabase | Firebase |
|---|---|---|
| 数据库类型 | PostgreSQL(关系型) | Firestore(文档型 NoSQL) |
| 开源性 | 完全开源 | Google 闭源产品 |
| 定价模式 | 固定 $25/月(Pro Plan) | 按用量计费(读/写/存储分开算) |
| 数据所有权 | 100% 持有,可随时导出 | 数据在 Google,导出麻烦 |
| 查询能力 | SQL 强大查询、复杂关联 | 查询受限,复杂关联要多次查询 |
| 实时功能 | WebSocket,需要手动订阅 | Firestore 原生实时,自动同步 |
| 离线支持 | 需自己实现缓存 | 原生离线支持,自动同步 |
| 迁移难度 | 标准 PostgreSQL,迁移容易 | 专有格式,迁移成本高 |
适用场景建议
选 Supabase 的场景:
- 数据关系复杂,需要 SQL 查询和关联
- 长期项目,希望数据完全可控
- 团队熟悉 SQL,想用 PostgreSQL
- 成本敏感,预算有限(固定费用比按用量稳定)
- 开源优先,希望技术栈可控
选 Firebase 的场景:
- 快速原型开发,时间紧迫
- 移动应用优先,离线功能很重要
- 实时同步是核心功能(比如聊天应用)
- 已经在用 Google Cloud 生态
- 数据结构简单,查询不复杂
说实话,我以前用 Firebase 做过几个项目,后来账单吓了我一跳——一个月 $800 多。后来迁移到 Supabase,同样的功能,成本稳定在 $25 左右。这个差距挺大的。
而且 Firebase 的数据导出是个麻烦事——Firestore 的数据格式是专有的,导出之后还得转换才能用。Supabase 就简单了,直接导出 SQL 文件,或者用 PostgreSQL 工具导出 CSV,随时能迁移到其他数据库。
不过 Firebase 在实时功能和移动应用这块确实强一些——Firestore 的实时同步、离线支持做得很好。如果你做聊天应用、协作文档这类强实时需求,Firebase 可能更顺手。
总结与建议
说了这么多,来收个尾。
Supabase 核心价值就这几样:
- PostgreSQL 数据库——强大查询、清晰关系
- 企业级认证——多种登录方式、JWT Token、RLS 权限
- 对象存储——简单好用、支持访问控制
- 实时同步、边缘计算——后续可以探索
而且它是开源的,数据完全可控,成本稳定——这些对长期项目来说挺重要。
如果你是前端开发者,想快速构建全栈应用,又不想被后端配置折腾,Supabase 是个不错的选择。基本上几小时就能搞定数据库、认证、存储三大功能——比传统后端开发快很多。
学习建议:
- 先把 Database、Auth、Storage 三块搞清楚,这是核心
- 然后试试 RLS Policy,这块能帮你理解认证和权限控制怎么串起来
- 实战项目是最好的学习方式——找个需求,从头搭建一个应用
- 后续再探索 Realtime、Edge Functions、Vector Database 这些高级功能
说到这,差不多就聊完了。Supabase 的官方文档写得挺清楚,遇到不懂的可以多看看:supabase.com/docs。还有社区里有很多教程和案例,比如 GitHub 上有个 awesome-supabase 列表,整理了很多资源。
嗯,一句话总结:如果你想用 PostgreSQL 做后端,又不想折腾配置,Supabase 帮你把路铺好了——直接走就行。
Supabase 三大核心功能快速上手
从零开始搭建 Supabase 项目,掌握 Database、Auth、Storage 三大核心功能
⏱️ 预计耗时: 2 小时
- 1
步骤1: 创建 Supabase 项目
前往 supabase.com 注册账号并创建新项目:
• 项目名称: my-first-app
• 数据库密码: 务必记下
• 区域: 选择离你最近的(国内选 Singapore 或 Tokyo)
创建完成后,在 Settings > API 获取 Project URL 和 Anon Public Key - 2
步骤2: 安装和初始化客户端
在前端项目中安装依赖:
npm install @supabase/supabase-js
初始化 Supabase Client:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
'https://your-project.supabase.co',
'your-anon-key'
); - 3
步骤3: 创建数据表和配置 RLS
使用 SQL Editor 创建表:
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
user_id UUID REFERENCES auth.users(id),
title TEXT NOT NULL,
status TEXT DEFAULT 'pending'
);
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;
CREATE POLICY 'Users can manage their own tasks'
ON tasks FOR ALL
USING (user_id = auth.uid());
RLS Policy 确保用户只能访问自己的数据 - 4
步骤4: 实现用户认证
Email/Password 认证代码示例:
// 注册
await supabase.auth.signUp({
email: '[email protected]',
password: 'password123'
});
// 登录
await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'password123'
});
// 获取当前用户
const { data: { user } } = await supabase.auth.getUser();
支持 Google、GitHub 等 20+ 平台社交登录 - 5
步骤5: 配置文件存储
创建 Storage Bucket(公开或私有):
// 上传文件
await supabase.storage
.from('avatars')
.upload('user-id/avatar.jpg', file);
// 获取公开 URL
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('user-id/avatar.jpg');
私有文件使用 createSignedUrl 生成临时访问链接
常见问题
Supabase 和 Firebase 有什么核心区别?
Supabase 适合什么样的项目?
• 数据关系复杂,需要 SQL 查询和关联
• 长期项目,希望数据完全可控
• 成本敏感,预算有限(固定费用更稳定)
• 前端开发者快速构建全栈应用
• 开源优先,希望技术栈可控
Row Level Security (RLS) 是什么?有什么用?
Supabase Auth 支持哪些登录方式?
• Email/Password(邮箱密码)
• Magic Link(无密码登录)
• OTP(一次性密码)
• Social Auth(Google、GitHub、Apple 等 20+ 平台)
• Phone Auth(Twilio、MessageBird)
• SSO(企业级单点登录)
Supabase Storage 的公开桶和私有桶有什么区别?
从 Firebase 迁移到 Supabase 需要多长时间?
Supabase 的免费计划包含什么?
16 分钟阅读 · 发布于: 2026年4月3日 · 修改于: 2026年4月4日
相关文章
Supabase 数据库设计:表结构、关系与 Row Level Security 完全指南
Supabase 数据库设计:表结构、关系与 Row Level Security 完全指南
防火墙配置:UFW、iptables 与安全策略设计
防火墙配置:UFW、iptables 与安全策略设计
shadcn/ui 常见问题排查:样式冲突、组件不渲染、类型错误

评论
使用 GitHub 账号登录后即可评论