Supabase使用演示

Supabase(https://supabase.com/)是一个开源的Baas平台(BaaS, Backend as a Service,后端即服务)。本文演示Supabase的使用流程。

**目标:**通过一个最基础的 Web 项目,熟悉Supabase 入门使用流程,让项目"能跑起来"。

这里使用比较常见的组合:Supabase + Next.js / React 的思路。


1. Supabase流程概览

Supabase 入门的核心就是:创建项目 → 建表 → 配置 RLS → 前端接入 SDK → 做认证 → 做 CRUD。

在 Supabase 控制台里创建一个项目后,就会得到:

  • 一个 PostgreSQL 数据库
  • 一个项目 URL
  • 一个匿名公钥(anon key)
  • 用户认证服务
  • 文件存储
  • 实时功能
  • 可选的 Edge Functions

前端通过 Supabase SDK 连接它,就能:

  • 注册/登录用户
  • 查询数据库
  • 插入数据
  • 上传文件
  • 订阅实时更新

2. 准备工作

使用Supabase之前,需要准备:

  • 一个 Supabase 账号(https://supabase.com/)
  • Node.js 环境
  • 一个前端项目(React / Next.js / Vue 都行)

当然,如果只是想体验Supabase,也可以先只在 Supabase 控制台里操作数据库,不写代码。


3. Supabase使用流程演示

3.1 Supabase部分

step1. 创建 Supabase 项目

1. 注册并登录: 去 Supabase 官网(https://supabase.com/)创建账号并登录。

**2. 创建一个新项目:**进入 dashboard 后

  • 点击 New Project
  • 选择组织
  • 输入项目名称
  • 输入数据库密码
  • 选择地区
  • 创建项目

创建完成后,Supabase 会帮你初始化 Postgres、Auth 等服务。


step2. 拿到项目连接信息

创建好项目后,去项目设置里找到:

  • Project URL
  • API Key
    • anon public key
    • service_role key

入门阶段主要先用:

  • SUPABASE_URL --> Project URL
  • SUPABASE_ANON_KEY --> anon public key

注意:

  • anon key 可以在前端使用
  • service_role key 绝对不要暴露到前端

step3. 创建一张测试表

我们先创建一个最简单的 todos 表。进入 Supabase 的 SQL Editor,执行下面 SQL:

sql 复制代码
create table todos (
  id bigint generated by default as identity primary key,
  created_at timestamp with time zone default timezone('utc'::text, now()) not null,
  title text not null,
  is_done boolean default false,
  user_id uuid references auth.users(id)
);

这张表表示:

  • id:主键
  • created_at:创建时间
  • title:待办标题
  • is_done:是否完成
  • user_id:属于哪个用户

step4. 开启行级安全(RLS)

Supabase 非常强调安全,建议从入门开始就养成配置 RLS(Row Level Security,行级安全)的习惯。

执行:

sql 复制代码
alter table todos enable row level security;

这样默认情况下,前端将不能随便读写这张表,除非你写 policy。


step5. 添加安全策略(Policy)

我们希望做到:

  • 用户只能看自己的 todos
  • 用户只能插入自己的 todos
  • 用户只能修改自己的 todos
  • 用户只能删除自己的 todos

执行下面 SQL:

sql 复制代码
-- 1. 允许查询自己的数据
create policy "Users can view their own todos"
on todos
for select
using (auth.uid() = user_id);

-- 2. 允许插入自己的数据
create policy "Users can insert their own todos"
on todos
for insert
with check (auth.uid() = user_id);

-- 3. 允许更新自己的数据
create policy "Users can update their own todos"
on todos
for update
using (auth.uid() = user_id);

-- 4. 允许删除自己的数据
create policy "Users can delete their own todos"
on todos
for delete
using (auth.uid() = user_id);

这一步非常关键。没有 policy,你前端即使连上了 Supabase,也可能查不到数据。

3.2 前端部分

step1. 创建前端项目

如果你想快速开始,可以用 Next.js:

bash 复制代码
npx create-next-app@latest my-supabase-app
cd my-supabase-app
npm install @supabase/supabase-js

如果你是普通 React 项目,也一样安装:

bash 复制代码
npm install @supabase/supabase-js

step2. 初始化 Supabase 客户端

在项目中创建一个文件,比如:

javascript 复制代码
// lib/supabase.js
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

然后在 .env.local 中写入:

bash 复制代码
NEXT_PUBLIC_SUPABASE_URL=你的项目URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=你的anon key

注意:

  • 在 Next.js 里,前端可读取的环境变量通常要以 NEXT_PUBLIC_ 开头。

step3. 用户注册/登录

Supabase Auth 的基础使用非常简单。

1. 邮箱注册

javascript 复制代码
const signUp = async () => {
  const { data, error } = await supabase.auth.signUp({
    email: 'test@example.com',
    password: '12345678',
  })

  if (error) {
    console.error('注册失败:', error.message)
  } else {
    console.log('注册成功:', data)
  }
}

2. 邮箱登录

javascript 复制代码
const signIn = async () => {
  const { data, error } = await supabase.auth.signInWithPassword({
    email: 'test@example.com',
    password: '12345678',
  })

  if (error) {
    console.error('登录失败:', error.message)
  } else {
    console.log('登录成功:', data)
  }
}

3. 获取当前用户

javascript 复制代码
const getUser = async () => {
  const { data, error } = await supabase.auth.getUser()

  if (error) {
    console.error(error.message)
  } else {
    console.log(data.user)
  }
}

4. 退出登录

javascript 复制代码
const signOut = async () => {
  const { error } = await supabase.auth.signOut()

  if (error) {
    console.error('退出失败:', error.message)
  }
}

step4. 插入一条数据

用户登录后,插入 todo:

javascript 复制代码
const addTodo = async () => {
  const {
    data: { user },
  } = await supabase.auth.getUser()

  if (!user) {
    console.log('请先登录')
    return
  }

  const { data, error } = await supabase
    .from('todos')
    .insert([
      {
        title: '学习 Supabase',
        user_id: user.id,
      },
    ])
    .select()

  if (error) {
    console.error('插入失败:', error.message)
  } else {
    console.log('插入成功:', data)
  }
}

这里必须传 user_id: user.id,否则会触发你前面设置的 RLS policy 校验失败。


step5. 查询当前用户的 todos

javascript 复制代码
const fetchTodos = async () => {
  const { data, error } = await supabase
    .from('todos')
    .select('*')
    .order('created_at', { ascending: false })

  if (error) {
    console.error('查询失败:', error.message)
  } else {
    console.log('todos:', data)
  }
}

你会发现:即使代码里没有写 where user_id = ...,由于 RLS policy 生效,用户也只能查到自己的数据。

当然,你也可以自己加过滤条件:

javascript 复制代码
const { data, error } = await supabase
  .from('todos')
  .select('*')
  .eq('is_done', false)

step6. 更新一条 todo

javascript 复制代码
const updateTodo = async (id) => {
  const { data, error } = await supabase
    .from('todos')
    .update({ is_done: true })
    .eq('id', id)
    .select()

  if (error) {
    console.error('更新失败:', error.message)
  } else {
    console.log('更新成功:', data)
  }
}

由于 RLS 限制,只有当前用户自己的 todo 才能更新成功。


step7. 删除一条 todo

javascript 复制代码
const deleteTodo = async (id) => {
  const { error } = await supabase
    .from('todos')
    .delete()
    .eq('id', id)

  if (error) {
    console.error('删除失败:', error.message)
  } else {
    console.log('删除成功')
  }
}

step8. 监听登录状态变化

很多项目里需要知道用户是否已登录,可以这样监听:

javascript 复制代码
supabase.auth.onAuthStateChange((event, session) => {
  console.log('auth event:', event)
  console.log('session:', session)
})

常见事件有:

  • SIGNED_IN
  • SIGNED_OUT
  • TOKEN_REFRESHED


step9. 最简单页面示例

下面是一个非常简化的 React 页面逻辑示例:

javascript 复制代码
'use client'

import { useEffect, useState } from 'react'
import { supabase } from './lib/supabase'

export default function Home() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [title, setTitle] = useState('')
  const [todos, setTodos] = useState([])
  //邮箱注册
  const signUp = async () => {
    const { error } = await supabase.auth.signUp({
      email,
      password,
    })
    if (error) alert(error.message)
    else alert('注册成功')
  }
  //邮箱登录
  const signIn = async () => {
    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    })
    if (error) alert(error.message)
    else {
      alert('登录成功')
      fetchTodos()
    }
  }
  //查询当前用户的 todos
  const fetchTodos = async () => {
    const { data, error } = await supabase
      .from('todos')
      .select('*')
      .order('created_at', { ascending: false })

    if (error) alert(error.message)
    else setTodos(data)
  }
  //插入一条数据
  const addTodo = async () => {
    const {
      data: { user },
    } = await supabase.auth.getUser()

    if (!user) {
      alert('请先登录')
      return
    }

    const { error } = await supabase.from('todos').insert([
      {
        title,
        user_id: user.id,
      },
    ])

    if (error) alert(error.message)
    else {
      setTitle('')
      fetchTodos()
    }
  }

  //useEffect
  useEffect(() => {
    fetchTodos()
  }, [])

  return (
    <main style={{ padding: 20 }}>
      <h1>Supabase 入门 Demo</h1>

      <div>
        <input
          placeholder="邮箱"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <input
          placeholder="密码"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
        <button onClick={signUp}>注册</button>
        <button onClick={signIn}>登录</button>
      </div>

      <hr />

      <div>
        <input
          placeholder="新的 todo"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
        <button onClick={addTodo}>添加</button>
      </div>

      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.title} - {todo.is_done ? '已完成' : '未完成'}
          </li>
        ))}
      </ul>
    </main>
  )
}

这个示例比较粗糙,但足以打通完整流程:

  • 注册
  • 登录
  • 插入数据
  • 查询数据

4. Supabase常见问题

Supabase 最常遇到的问题基本就这几个:

4.1 没开启 RLS 或 policy 没写对

表现:

  • 查询为空
  • 插入失败
  • 提示权限问题

建议:

  • 检查表是否 enable row level security
  • 检查 policy 中 auth.uid() = user_id 是否正确
  • 确认用户真的已经登录

4.2 前端没拿到登录态

表现:

  • getUser() 返回空
  • 插入时 user_id 没值

建议:

  • 确认登录成功
  • 检查 session 是否存在
  • 页面刷新后重新获取用户信息

4.3 环境变量写错

表现:

  • SDK 初始化失败
  • 请求报错
  • 连接不到项目

建议:

  • 检查 URL
  • 检查 anon key
  • 重启开发服务器

4.4 service_role key 放到前端

这是严重安全问题。

前端只能用 anon key,不要把 service_role 暴露出去。


5. 进阶学习

推荐的Supabase学习顺序:

  1. 创建项目
  2. 建表
  3. 开启 RLS
  4. 写 policy
  5. 实现邮箱注册/登录
  6. 做一套 CRUD
  7. 做用户 profile 表
  8. 接入文件上传
  9. 接入实时订阅
  10. 最后再学 Edge Functions

当入门流程通过后,建议继续学下述这几个方向:

1. Auth 进阶

  • 邮箱验证
  • OAuth 登录(Google/GitHub)
  • 重置密码
  • 用户资料表设计

2. 数据库设计

  • 外键
  • 索引
  • 视图
  • 存储过程 / SQL function

3. RLS 深入

  • 多角色权限
  • 管理员权限
  • 团队空间权限
  • 公有/私有数据混合访问

4. Storage

  • 上传头像
  • 上传文章封面
  • 私有文件访问

5. Edge Functions

  • Webhook
  • 服务端逻辑
  • 调用第三方 API
  • 支付回调

6. 相关文档

  1. Baas(后端即服务)简介: https://blog.csdn.net/taotiezhengfeng/article/details/159115849

  2. 开源Baas平台 Supabase 简介:https://blog.csdn.net/taotiezhengfeng/article/details/159116106

  3. 开源Baas平台 Supabase 入门演示:https://blog.csdn.net/taotiezhengfeng/article/details/159116342

相关推荐
aZhe的全栈知识分享2 小时前
OpenClaw(龙虾)太难装?这份保姆级教程让你 3 分钟搞定
前端·人工智能·后端
2301_764441332 小时前
ProjectAIRI:是一个开源的AI虚拟数字人伴侣
人工智能·目标检测·自然语言处理·开源·视觉检测·语音识别
Java编程爱好者2 小时前
Claude Code 最佳实践:可验证、可治理、可分层的工程现实
后端
写Cpp的小黑黑2 小时前
Cursor Chrome DevTools MCP 配置指南 for macOS
后端
神奇小汤圆2 小时前
为什么 synchronized 不能防止指令重排序?
后端
AMoon丶2 小时前
Golang--锁
linux·开发语言·数据结构·后端·算法·golang·mutex
大雷神2 小时前
HarmonyOS APP<玩转React>开源教程十:组件化开发概述
前端·react.js·开源·harmonyos
神奇小汤圆2 小时前
Java面试被问:跟我讲下JVM和JMM?
后端
李日灐3 小时前
改造红黑树实现封装 map/set:感受C++ 标准容器的精妙设计与底层实现
开发语言·数据结构·c++·后端·算法·红黑树