切换语言
切换主题

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 表:

字段名类型约束
idint8Primary Key, Auto Increment
emailtextUnique, Not Null
nametext-
created_attimestamptzDefault: 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。

每个平台的配置不一样,不过大致步骤类似:

  1. 在 Google/GitHub 创建 OAuth App
  2. 复制 Client ID 和 Client Secret 到 Supabase
  3. 配置回调 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.jpgreport.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 到底选哪个?嗯,这块详细对比一下。

核心差异

对比维度SupabaseFirebase
数据库类型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

    步骤1: 创建 Supabase 项目

    前往 supabase.com 注册账号并创建新项目:

    • 项目名称: my-first-app
    • 数据库密码: 务必记下
    • 区域: 选择离你最近的(国内选 Singapore 或 Tokyo)

    创建完成后,在 Settings > API 获取 Project URL 和 Anon Public Key
  2. 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

    步骤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

    步骤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

    步骤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 使用 PostgreSQL 关系型数据库,支持复杂 SQL 查询和数据关联;Firebase 使用 Firestore 文档型 NoSQL,查询能力受限。Supabase 完全开源、固定定价 $25/月、数据 100% 可控;Firebase 闭源、按用量计费(可能高达 $800+/月)、数据导出麻烦。
Supabase 适合什么样的项目?
适合以下场景:

• 数据关系复杂,需要 SQL 查询和关联
• 长期项目,希望数据完全可控
• 成本敏感,预算有限(固定费用更稳定)
• 前端开发者快速构建全栈应用
• 开源优先,希望技术栈可控
Row Level Security (RLS) 是什么?有什么用?
RLS 是 PostgreSQL 的行级安全特性,可以在数据库层面控制用户只能访问自己的数据。配合 Supabase Auth 的 auth.uid() 函数,实现用户登录后只能看到/修改自己创建的记录,比前端代码判断更安全。
Supabase Auth 支持哪些登录方式?
支持多种登录方式:

• Email/Password(邮箱密码)
• Magic Link(无密码登录)
• OTP(一次性密码)
• Social Auth(Google、GitHub、Apple 等 20+ 平台)
• Phone Auth(Twilio、MessageBird)
• SSO(企业级单点登录)
Supabase Storage 的公开桶和私有桶有什么区别?
公开桶(Public Bucket)的文件任何人都能访问,适合公开图片、头像等;私有桶(Private Bucket)的文件需要授权才能访问,使用 createSignedUrl 生成临时访问链接(可设置有效期),适合私密文档、用户上传文件等。
从 Firebase 迁移到 Supabase 需要多长时间?
简单应用 2-3 天就能完成迁移:Firestore 数据转为 PostgreSQL 表结构、Firebase Auth 用户导出到 Supabase Auth、Cloud Storage 文件迁移到 Supabase Storage。复杂应用可能需要 1-2 周,主要工作量在数据模型转换和查询逻辑重写。
Supabase 的免费计划包含什么?
免费计划包含:无限 API 请求、50,000 月活用户、500MB 数据库存储、1GB 文件存储、5GB 带宽。适合个人 MVP 开发,超出限制需升级到 Pro Plan($25/月)。

16 分钟阅读 · 发布于: 2026年4月3日 · 修改于: 2026年4月4日

评论

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

相关文章