用 Supabase 打造全栈应用后端
用 Supabase 打造全栈应用后端 #
Supabase 是一个开源的 Firebase 替代方案,提供了 PostgreSQL 数据库、认证、存储、实时订阅等全套后端服务。本文将带你深入了解如何用 Supabase 快速搭建全栈应用后端。
什么是 Supabase #
Supabase 提供了以下核心功能:
- PostgreSQL 数据库 — 功能强大的关系型数据库
- 身份认证 — 支持邮箱、社交登录、Magic Link
- 对象存储 — 存储文件、图片、视频
- 实时订阅 — 数据变更实时推送
- Edge Functions — 服务端函数
- Row Level Security — 行级安全策略
Supabase 让独立开发者也能拥有企业级的后端基础设施。
创建 Supabase 项目 #
步骤一:注册并创建项目 #
- 访问 supabase.com 注册账号
- 点击 “New Project” 创建新项目
- 选择离你最近的数据库区域(推荐东亚地区选择 Singapore)
- 设置数据库密码并记住它
步骤二:安装客户端 #
npm install @supabase/supabase-js
步骤三:初始化客户端 #
// lib/supabase.ts
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)
数据库设计 #
创建表结构 #
在 Supabase Dashboard 的 SQL Editor 中执行:
-- 文章表
create table posts (
id uuid default gen_random_uuid() primary key,
slug text unique not null,
title text not null,
content text,
excerpt text,
category text,
status text default 'draft',
cover_image_url text,
word_count integer default 0,
published_at timestamptz,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
-- 标签表
create table tags (
id uuid default gen_random_uuid() primary key,
name text unique not null
);
-- 文章-标签关联表
create table post_tags (
post_id uuid references posts(id) on delete cascade,
tag_id uuid references tags(id) on delete cascade,
primary key (post_id, tag_id)
);
CRUD 操作 #
查询文章列表 #
async function getPublishedPosts() {
const { data, error } = await supabase
.from("posts")
.select("*")
.eq("status", "published")
.order("published_at", { ascending: false })
if (error) throw error
return data
}
根据 slug 获取单篇文章 #
async function getPostBySlug(slug: string) {
const { data, error } = await supabase
.from("posts")
.select(`*, post_tags ( tags ( name ) )`)
.eq("slug", slug)
.single()
if (error) throw error
return data
}
创建新文章 #
async function createPost(post: {
title: string; slug: string; content: string; excerpt: string
}) {
const { data, error } = await supabase
.from("posts").insert(post).select().single()
if (error) throw error
return data
}
身份认证 #
实现登录功能 #
// 邮箱密码登录
async function signIn(email: string, password: string) {
const { data, error } = await supabase.auth.signInWithPassword({ email, password })
if (error) throw error
return data
}
// GitHub OAuth 登录
async function signInWithGitHub() {
const { data, error } = await supabase.auth.signInWithOAuth({ provider: "github" })
if (error) throw error
return data
}
Row Level Security (RLS) #
-- 启用 RLS
alter table posts enable row level security;
-- 所有人可以读取已发布文章
create policy "Published posts are viewable by everyone"
on posts for select using (status = 'published');
-- 只有认证用户可以创建文章
create policy "Authenticated users can create posts"
on posts for insert to authenticated with check (true);
注意: RLS 是 Supabase 安全的核心。务必为每个表启用 RLS 并配置合适的策略。
对象存储 #
async function uploadImage(file: File) {
const fileName = `${Date.now()}-${file.name}`
const { data, error } = await supabase.storage
.from("images").upload(fileName, file)
if (error) throw error
const { data: urlData } = supabase.storage
.from("images").getPublicUrl(data.path)
return urlData.publicUrl
}
实时订阅 #
const channel = supabase
.channel("posts-changes")
.on("postgres_changes",
{ event: "*", schema: "public", table: "posts" },
(payload) => { console.log("Change received:", payload) }
)
.subscribe()
总结 #
Supabase 为全栈开发提供了一站式解决方案。相比自己搭建后端服务,Supabase 能让你把更多精力放在产品本身。