首页
/ 用 Refine 与 Supabase 在 5 分钟内搭建企业级 Admin Panel

用 Refine 与 Supabase 在 5 分钟内搭建企业级 Admin Panel

2026-09-09 19:52:38作者:韦蓉瑛

导读

本篇技术指南围绕 documentation/blog/2025-11-12-admin-panel-in-5-minutes.md 的核心思路展开:以 Supabase 作为开箱即用的 PostgreSQL 后端(认证、自动生成的 REST API、实时订阅、文件存储),以 Refine 作为前端框架,把"连接数据库 → AI 分析 schema → 生成生产级 CRUD 页面 → 接通认证与实时更新"这一整套流程压缩到几分钟内。读完本文,你将掌握:如何用 Refine 官方启动向导连接 Supabase、如何让 AI 分析数据库表结构并生成可定制的列表/创建/编辑页面,以及 Refine 内置 Supabase data provider、live provider 与 auth provider 的底层实现原理与关键配置。


为什么是 Supabase?

Supabase 是一个构建在 PostgreSQL 之上的后端即服务(Backend-as-a-Service)平台。对你来说,它直接提供了一整套 admin panel 必需的基础设施:

  • 真实的 PostgreSQL 数据库,而不是某种私有数据格式,schema、外键、约束全部可用 SQL 管理;
  • 自动生成的 REST API(基于 PostgREST),表一建好就有对应的 CRUD 端点;
  • 内置认证(Auth),支持邮箱密码、OAuth 社交登录、会话持久化;
  • 实时订阅(Realtime),数据库变化可以实时推送到前端;
  • 文件存储(Storage),可直接对接图片、附件上传。

对 admin panel 场景而言,Supabase 最省心的地方在于"什么都不缺"——你不需要花一天时间搭建认证、不需要手动设计 API 端点,这些都已经就绪。配合 Refine,你只需要把前端指向你的 Supabase 数据库,剩下的 CRUD 页面、表单、路由都由 Refine 生成。


Refine 如何把"项目"变成"几分钟"

Refine 是一个用于构建内部工具、Admin Panel、Dashboard 与 B2B 应用的 React 框架。而本博客描述的工作流,是 Refine 的 AI 辅助生成能力:你把数据库连上去,它分析你的 schema,然后你像和一名精通 Refine Core 的开发者对话一样,用聊天或建议选项直接生成页面。生成结果不是一次性原型代码,而是生产级、TypeScript、遵循最佳实践、可继续定制的 React 代码。

这种能力本质上建立在 Refine 成熟的包体系之上。以 Supabase 集成为例,仓库中的 packages/supabase 包集中提供了三样东西:

模块 文件 作用
data provider packages/supabase/src/dataProvider/index.ts 把 Refine 的 CRUD 数据钩子映射到 Supabase/PostgREST API
live provider packages/supabase/src/liveProvider/index.ts 基于 Supabase Realtime 的实时订阅
createClient packages/supabase/src/index.ts 再导出 @supabase/supabase-js 客户端工厂

也就是说,AI 生成的页面背后,是这些经过反复测试的 provider 在负责"接线",这也是它能做到又快又稳的原因。


Step 1:创建项目并连接 Supabase

在 Refine 的启动页面(refine.dev/start)点击 Connect to Supabase,然后授权并选择你要用作后端数据库的 Supabase 项目即可。整个过程不需要写任何初始化代码——连接建立后,Refine 会拿到你的数据库访问凭证。

传统手工方式的等价物,是仓库示例 examples/data-provider-supabase 中由 CLI 自动生成的 supabaseClient.ts

// examples/data-provider-supabase/src/utility/supabaseClient.ts
import { createClient } from "@refinedev/supabase";

const SUPABASE_URL = "https://iwdfzvfqbtokqetmbmbp.supabase.co";
const SUPABASE_KEY =
  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzMDU2NzAxMCwiZXhwIjoxOTQ2MTQzMDEwfQ._gr6kXGkQBi9BM9dx5vKaNKYj_DJN1xlkarprGpM_fU";

export const supabaseClient = createClient(SUPABASE_URL, SUPABASE_KEY, {
  db: {
    schema: "public", // 可通过 data hooks 的 meta.schema 覆盖
  },
  auth: {
    persistSession: true,
  },
});

值得注意的配置点:

  • db.schema:默认使用 public schema,可以在单个 data hook 上通过 meta.schema 覆盖(见下文"meta 参数"一节)。
  • auth.persistSession:会话持久化开关,决定刷新页面后用户是否保持登录。
  • 凭证安全:建议把 URL 和 Key 放进环境变量,而不是硬编码在源码里。

随后在 App.tsx 中把客户端注册为 Refine 的 data provider:

import { Refine } from "@refinedev/core";
import { dataProvider } from "@refinedev/supabase";
import { supabaseClient } from "./utility/supabaseClient";

function App() {
  return (
    <Refine
      dataProvider={dataProvider(supabaseClient)}
      // ...
    >
      {/* ... */}
    </Refine>
  );
}

export default App;

底层:data provider 是如何工作的

从源码看,dataProvider(supabaseClient) 返回一个完整的、实现全部数据方法(getListgetManycreatecreateManyupdateupdateManygetOnedeleteOnedeleteMany)的对象,见 packages/supabase/src/dataProvider/index.ts

几个关键实现细节:

  • schema 支持:每个方法都会检查 meta?.schema,若存在则调用 supabaseClient.schema(meta.schema) 切换到指定 schema(dataProvider/index.ts#L16-L18)。
  • 分页getList 默认 currentPage = 1, pageSize = 10,在 mode === "server" 时通过 .range() 做服务端分页(dataProvider/index.ts#L24-L26)。
  • 排序:支持对关联表的字段排序,例如 categories.title 会被拆分为外键表与字段,并调用 .order(field, { foreignTable })dataProvider/index.ts#L28-L43)。
  • 计数getList 默认使用 count: "exact",可通过 meta.count 改为 "estimated" 以提升大表性能。
  • 主键列名:默认按 id 列查询,可通过 meta.idColumnName 指定自定义主键。
  • getApiUrlcustom:这两个方法在 Supabase data provider 中不实现,会直接抛错(dataProvider/index.ts#L256-L262),因为 Supabase 通过 PostgREST 已覆盖全部所需数据操作。

底层:过滤器是如何翻译的

Refine 的通用 CrudFilter 会在 packages/supabase/src/utils/generateFilter.ts 中被翻译成 Supabase 查询:

Refine 操作符 Supabase 方法 语义
eq / ne query.eq / query.neq 等于 / 不等于
in query.in 属于集合
gt / gte / lt / lte query.gt 大小比较
between query.gte(...).lte(...) 区间(要求值长度为 2,否则抛错)
contains / containss query.ilike / query.like 模糊匹配(大小写不敏感 / 敏感)
null query.is(field, null) 为空
startswith / endswith query.ilike 前缀 / 后缀匹配
or query.or 组合 OR 条件
and 抛错 不支持

Step 2:让 AI 分析你的数据库

连接 Supabase 项目后,点击 Continue,Refine 的 AI 会分析你的 schema,并把分析结果展示给你确认。这一步你会看到:

  • 所有表及其结构;
  • 每列的类型(text、数字、日期、UUID 等);
  • 表与表之间的外键关系;
  • 约束与默认值。

在最终点击 Continue 之前,值得最后过一遍这些信息——因为后续生成的页面、表单字段、下拉关联(如 posts -> categories)全部由这份分析驱动。schema 里的外键关系越清晰,生成的关联字段就越准确。


Step 3:生成生产级代码

分析完成后,你会进入项目起始页,AI 会给出几个建议动作(例如"创建带数据表格的 Employee 列表页")。你可以直接用聊天框下达自己的指令,也可以直接采纳建议。

以本博客的演示为例:先选择创建 Employee 列表页。不到一分钟,AI 就生成一个完全可浏览、可交互的列表资源页——表格、排序、筛选、操作列一应俱全。不满意布局或数据展示方式?直接告诉 AI 调整,Refine 负责接线。如果一路跟随建议,一个完整的 admin panel 在一小时内就能成型。

手工等价物:一个典型的 List 页面

如果不用 AI,手工创建列表页的典型形态(来自 documentation/docs/data/packages/supabase/index.mdexamples/data-provider-supabase/src/pages/posts/list.tsx):

import { List, useTable, EditButton, ShowButton } from "@refinedev/antd";
import { Table, Space } from "antd";

export const PostList: React.FC = () => {
  const { tableProps, sorters } = useTable<IPost>({
    sorters: { initial: [{ field: "id", order: "asc" }] },
    meta: {
      select: "*, categories(title)", // 关联 categories 表并取 title 字段
    },
  });

  return (
    <List>
      <Table {...tableProps} rowKey="id">
        <Table.Column key="id" dataIndex="id" title="ID" sorter />
        <Table.Column key="title" dataIndex="title" title="Title" sorter />
        <Table.Column
          key="categoryId"
          dataIndex={["categories", "title"]}
          title="Category"
        />
        <Table.Column<IPost>
          title="Actions"
          dataIndex="actions"
          render={(_, record) => (
            <Space>
              <EditButton hideText size="small" recordItemId={record.id} />
              <ShowButton hideText size="small" recordItemId={record.id} />
            </Space>
          )}
        />
      </Table>
    </List>
  );
};

AI 生成页面时,本质上就是按同样的模式(useTable + meta.select + Refine 的 UI 组件)为你铺好初始代码,然后把布局微调交给对话式交互完成。


认证:authProvider 与 Supabase Auth

AI 生成的工程同样包含完整的认证闭环。Refine 的 auth provider 概念允许接入任意认证服务;对 Supabase,CLI 会生成一个 authProvider.ts,其内部直接调用 Supabase Auth API:

auth provider 方法 底层 Supabase API
login auth.signInWithOAuth(provider)(社交登录)或 auth.signInWithPassword({ email, password })
register auth.signUp({ email, password })
forgotPassword auth.resetPasswordForEmail(email, { redirectTo })
updatePassword auth.updateUser({ password })
logout auth.signOut()
check auth.getSession() 判断是否存在有效会话
getPermissions auth.getUser() 返回用户角色
getUserIdentity auth.getUser() 返回用户信息(以 email 作为 name)

完整的参考实现在 documentation/docs/data/packages/supabase/index.md 的 "Understanding the Auth Provider" 一节,示例工程见 examples/data-provider-supabase/src

注册 authProvider 并接入 AuthPage

import { Refine, Authenticated } from "@refinedev/core";
import { AuthPage } from "@refinedev/antd";
import routerProvider, { CatchAllNavigate } from "@refinedev/react-router";
import authProvider from "./authProvider";

function App() {
  return (
    <Refine routerProvider={routerProvider} authProvider={authProvider}>
      <Routes>
        <Route
          element={
            <Authenticated fallback={<CatchAllNavigate to="/login" />}>
              <ThemedLayout>
                <Outlet />
              </ThemedLayout>
            </Authenticated>
          }
        >
          <Route path="/posts" element={<PostList />} />
        </Route>
        <Route path="/login" element={<AuthPage type="login" />} />
        <Route path="/register" element={<AuthPage type="register" />} />
        {/* forgot-password / update-password 同理 */}
      </Routes>
    </Refine>
  );
}

<AuthPage> 是开箱即用的登录/注册/忘记密码/更新密码页面,它的按钮会自动绑定到 authProvider 对应方法上。

添加 Google 社交登录

只需在 <AuthPage> 上配置 providers

<AuthPage
  type="login"
  providers={[
    {
      name: "google",
      label: "Sign in with Google",
      icon: <GoogleOutlined style={{ fontSize: 18, lineHeight: 0 }} />,
    },
  ]}
/>

同时在 Supabase 控制台(Authentication → Settings → Auth Providers)启用 Google Auth 并填入你的 Google OAuth 凭证。登录成功后,Refine 会把用户重定向回应用。


实时更新:Supabase Realtime + Refine Live Provider

Supabase 内置 Realtime 能力:当记录被创建、更新或删除时,变更可以实时推送到前端。Refine 的 Supabase live provider 在 packages/supabase/src/liveProvider/index.ts 中封装了订阅逻辑:

  • 事件类型映射:INSERT → createdUPDATE → updatedDELETE → deleted(见 packages/supabase/src/types/index.ts);
  • 订阅通过 channel("resources/{resource}") 建立,内部注册 postgres_changes 监听;
  • 支持通过 params.ids 过滤(只对已存在的 id 触发回调),以及通过 params.filters 生成 Realtime 过滤串(liveProvider/index.ts#L52-L80);
  • 需要注意:Supabase Realtime 目前只支持单个 filter 字符串,多个过滤器时仅使用第一个并打印警告。

启用方式——在 <Refine> 上注册 live provider,并把 liveMode 设为 auto

import { Refine } from "@refinedev/core";
import { liveProvider } from "@refinedev/supabase";
import { supabaseClient } from "./utility/supabaseClient";

function App() {
  return (
    <Refine
      liveProvider={liveProvider(supabaseClient)}
      options={{ liveMode: "auto" }}
      // ...
    />
  );
}

liveMode: "auto" 意味着列表、详情等页面在数据变化时自动刷新;如果只想在特定页面手动控制,也可以用 "manual" 配合 onLiveEvent 自行处理(编辑页的"数据已过期,请刷新"提示就是这种模式)。


通过 meta 深度控制数据查询

Supabase data provider 的灵活性很大程度上来自 meta 参数。以下用法在 documentation/docs/data/packages/supabase/index.md 中有完整说明,也都能在 packages/supabase/src/dataProvider/index.ts 中找到对应实现:

select —— 指定返回字段

默认查询方法用 *(全部字段),通过 meta.select 可以裁剪:

useList({
  resource: "posts",
  meta: { select: "title, content" },
});

select —— 一对多关联

posts -> categories 关系下,直接取关联表的字段:

useTable<IPost>({
  resource: "posts",
  meta: { select: "*, categories(title)" },
});

select —— 多对多关联

movies <-> categories_movies <-> categories 场景下用 !inner 强制内连接:

useTable({
  resource: "movies",
  meta: { select: "*, categories!inner(name)" },
});

idColumnName —— 自定义主键列

表主键不叫 id 时(例如 post_id):

useMany({
  resource: "posts",
  ids: [1, 2],
  meta: { idColumnName: "post_id" },
});

schema —— 自定义 schema

默认使用 supabaseClient 里配置的 schema,可在单个钩子上覆盖:

useTable({
  resource: "posts",
  meta: { schema: "foo" },
});

深层过滤

按关联表字段过滤时,meta.select 必须用 !inner 才能让过滤生效:

useTable({
  resource: "posts",
  filters: {
    initial: [{ field: "categories.title", operator: "eq", value: "Beginning" }],
  },
  meta: { select: "*, categories!inner(title)" },
});

count —— 提升 getList 性能

默认 exact 精确计数可能拖慢大表请求,可改用估算:

useList({
  resource: "posts",
  meta: { count: "estimated" },
});

一个已知限制

Supabase 客户端本身不支持对关联表数据做 Realtime 订阅。如果列表用了 select: "*, categories(title)",关联表变化不会自动触发刷新,需要手动订阅并 refetch:

import { useTable, useSubscription } from "@refinedev/core";

export const PostList = () => {
  const table = useTable({
    meta: { select: "*, categories(title)" },
  });

  useSubscription({
    channel: "categories",
    types: ["*"],
    onLiveEvent: () => {
      table.tableQuery.refetch();
    },
  });

  return <>{/* ... */}</>;
};

为什么这套工作流值得采用

传统的 admin panel 开发流程是:初始化项目 → 安装依赖 → 配置 data provider → 为每个资源分别编写 list / create / edit / show 页面 → 接路由……对每个资源重复一遍。这不难,但耗时且重复。

Refine + Supabase 的 AI 工作流改变了这个过程:

  • 真正快速交付:原本要数天的内部工具,一个下午就能完成;客户 Dashboard 半天内上线;需要快速迭代的 MVP 不再被脚手架拖累。
  • 把时间花在关键处:CRUD、表格、表单是"已解决的问题"。AI 负责脚手架,你把精力放在业务逻辑、自定义工作流、集成等真正差异化部分。
  • 提升生产力而非替代人:AI 是处理重复部分的结对程序员。架构决策、自定义逻辑仍然由你掌控,只是不必再第 N 次重写数据表格。

packages/supabase 的源码可以看到,这套"分钟级"体验背后是扎实的工程基础:data provider 完整覆盖 CRUD 与过滤语义,live provider 封装 Realtime 事件映射,auth provider 对接完整认证流程。你可以在生成代码后继续深入定制——例如参考 examples/data-provider-supabase 的完整示例工程,查看带文件上传(supabaseClient.storage)、关联下拉、实时刷新提示的真实实现。

如果你想复现整个流程,只需要:一个 Supabase 免费项目 + Refine 启动向导 → 连接数据库 → AI 分析 schema → 生成页面 → 按需接入认证与实时订阅。从连接数据库到拥有可用的 admin panel,整个过程可以压缩在几分钟之内——这就是本博客标题"5 分钟"的含义。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
docsdocs
暂无描述
Markdown
899
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
525
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395