- 浏览器里怎么连 Supabase
- 服务端怎么连 Supabase
- 中间件里怎么连 Supabase
- 管理员权限怎么连 Supabase
- 环境变量对不对怎么检查
1. client.ts:浏览器端客户端
这个文件一般是给 前端页面 / 客户端组件 用的。
比如用户在网页里:
- 登录
- 注册
- 获取当前用户
- 查询自己能看到的数据
通常会在这里创建一个“浏览器版 Supabase 客户端”。
典型作用
"use client";
import { createBrowserClient } from "@supabase/ssr";export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}
它的特点
- 运行在浏览器
- 用的是
anon key - 会带上当前用户登录态
- 权限受 RLS 限制
适合做什么
比如前端页面里:
const supabase = createClient();
const { data } = await supabase.from("posts").select("*");
这个查询能不能成功,要看当前用户权限和 RLS 策略。
2. server.ts:服务端客户端
这个文件一般给 Next.js 服务端 用,比如:
- Server Components
- Server Actions
- Route Handlers
- API 路由
为什么要单独有它
因为服务端和浏览器不一样:
- 服务端可以读取 cookie
- 服务端可以拿到当前登录用户 session
- 服务端不直接暴露 key 给前端
常见写法
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";export async function createClient() {
const cookieStore = await cookies(); return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
} catch {}
},
},
}
);
}
它的特点
- 运行在服务器
- 也一般用
anon key - 但可以通过 cookie 识别当前用户
- 适合安全地在服务端读取用户信息
适合做什么
例如:
const supabase = await createClient();
const {
data: { user },
} = await supabase.auth.getUser();
这种就很适合在服务端判断当前登录用户是谁。
3. middleware.ts:中间件专用客户端
这个文件一般是给 Next.js middleware 用的。
middleware 的作用是:
在请求真正进入页面之前,先拦截一下,做登录校验、刷新 session、跳转等。
它常做什么
- 检查用户是否已登录
- 刷新 Supabase 的 auth session
- 未登录就重定向到登录页
为什么单独写
因为 middleware 的运行环境又和普通 server 不一样。
它要处理的是 NextRequest 和 NextResponse,cookie 的读写方式也不一样。
典型场景
比如用户访问:
/dashboard/user/profile
你想要求必须登录才能访问。
那 middleware 会先跑:
const supabase = createMiddlewareClient(...)
const { data: { user } } = await supabase.auth.getUser()if (!user) {
return NextResponse.redirect("/sign-in")
}
你可以把它理解成
它是网站的“门卫”。
4. admin.ts:管理员客户端
这个非常重要。
它一般是用 service role key 创建的客户端,不是普通用户客户端。
它的特点
- 运行在服务端
- 权限非常高
- 绕过 RLS
- 可以操作所有用户的数据
常见写法
import { createClient } from "@supabase/supabase-js";export function createAdminClient() {
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
}
为什么危险
因为 service_role_key 相当于数据库超级钥匙。
它能做的事很多,比如:
- 查所有用户数据
- 改任何人的积分
- 删除任何记录
- 绕过前端权限控制
所以必须注意
- 只能在服务端用
- 绝对不能暴露到前端
- 不能写进客户端组件
- 不能返回给浏览器
适合做什么
比如你之前那种积分扣减逻辑:
const adminClient = await createAdminClient();await adminClient
.from("ai_images_creator_credits")
.update({ credits: 4 })
.eq("user_id", user.id);
或者调用 RPC、操作后台表。
5. check-env-vars.ts:检查环境变量
这个文件一般是拿来检查你项目所需的环境变量有没有配好。
比如 Supabase 项目一般需要:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY
为什么需要它
因为很多报错其实不是代码错,而是:
.env.local没写- 拼写错了
- key 复制错了
- 少配了变量
这个文件一般会做什么
export function checkEnvVars() {
if (!process.env.NEXT_PUBLIC_SUPABASE_URL) {
throw new Error("Missing NEXT_PUBLIC_SUPABASE_URL");
} if (!process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) {
throw new Error("Missing NEXT_PUBLIC_SUPABASE_ANON_KEY");
}
}
作用
项目启动时先检查,避免你运行半天才发现环境变量没配。
这几个文件之间的关系
你可以把它们理解成一套“分工明确的钥匙”:
client.ts
给前端页面用的小钥匙server.ts
给服务端用的钥匙middleware.ts
给门卫用的钥匙admin.ts
给管理员用的万能钥匙check-env-vars.ts
检查钥匙和门牌号有没有写对
一个最常见的实际分工例子
用户在前端点击登录
前端页面里用:
import { createClient } from "@/lib/supabase/client";
服务端页面读取当前用户
服务端用:
import { createClient } from "@/lib/supabase/server";
路由保护
middleware 里用:
import { updateSession } from "@/lib/supabase/middleware";
后台操作积分、订单、所有用户数据
服务端私密逻辑里用:
import { createAdminClient } from "@/lib/supabase/admin";
为什么要拆成这么多文件
因为 同样是“连接 Supabase”,不同环境的规则不一样。
如果你全写在一个文件里,会很乱,而且容易犯错,比如:
- 在前端误用了
service_role_key - 在服务端没正确处理 cookie
- middleware 里用了不适配的方法
所以拆开之后:
- 更安全
- 更清晰
- 更符合 Next.js 项目结构
- 更适合团队协作
你现在最需要重点理解的两个
如果你是做 Next.js + Supabase 项目,最关键先搞懂:
client.ts
这是前端拿数据、登录注册常用的
server.ts
这是服务端拿当前用户、保护数据最常用的
然后再理解:
admin.ts
这是高权限后台逻辑
一句话总结
你这个 supabase 文件夹,不是单纯“一个 SDK”,而是:
对 Supabase 官方 SDK 的项目级封装,用来适配前端、服务端、中间件、管理员权限和环境变量检查。