前端转全栈 14:综合实战——从 0 到 1 做一个带用户系统的全栈应用

前端全栈tutorial实战项目nextjssupabasecapstone

系列目录:本文是「前端转全栈:Next.js + Supabase 实战」系列的第 14 篇,也是综合实战篇。前面每篇是零件,这一篇把它们组装成一辆能上路的车。


学了很多概念,但「真要我独立做一个,从哪下手?」——这篇给你一条可复制的做事流水线。我们以一个「带登录的留言板」为例子,它覆盖了全栈应用的全部核心要素。


一、需求拆解(先做减法)

MVP(最小可行产品)只保留:

  • 游客能看留言列表
  • 登录用户能发留言、删自己的留言
  • 数据持久化

不做的(留作扩展):富文本、点赞、通知、管理后台。全栈新手最容易死在「想一次做全」,先交付核心闭环。


二、数据建模(第 3 篇)

一张表就够:

CREATE TABLE messages (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  content TEXT NOT NULL CHECK (char_length(content) <= 500),
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
  created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_messages_created_at ON messages (created_at DESC);
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
CREATE POLICY "公开可读" ON messages FOR SELECT USING (true);
CREATE POLICY "登录可写" ON messages FOR INSERT
  WITH CHECK (auth.role() = 'authenticated');
CREATE POLICY "删自己的" ON messages FOR DELETE
  USING (auth.uid() = user_id);

注意 CHECK (char_length <= 500) 在数据库层就限制了长度——防御纵深


三、页面与数据获取(第 4、5 篇)

列表用 Server Component 直连库,无需 API 中转:

// app/page.tsx(Server Component)
import { createClient } from "@/lib/supabase/server"
export default async function Home() {
  const supabase = await createClient()
  const { data: messages } = await supabase
    .from("messages").select("content, created_at, user_id").order("created_at", { ascending: false })
  return <MessageList initial={messages} />
}

四、写操作:Server Action(第 8 篇)

// app/actions.ts
"use server"
import { createClient } from "@/lib/supabase/server"
import { z } from "zod"
import { revalidatePath } from "next/cache"

const Schema = z.object({ content: z.string().min(1).max(500) })

export async function addMessage(prev: any, formData: FormData) {
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()
  if (!user) return { error: "请先登录" }
  const parsed = Schema.safeParse({ content: String(formData.get("content")) })
  if (!parsed.success) return { error: "内容不合法" }
  const { error } = await supabase.from("messages")
    .insert({ content: parsed.data.content, user_id: user.id })
  if (error) return { error: error.message }
  revalidatePath("/")
  return { ok: true }
}

这里同时用到了:认证校验(第 7 篇)+ zod 校验(第 6 篇)+ 渐进增强表单(第 8 篇)


五、认证入口(第 7 篇)

登录按钮调 GitHub OAuth,回调用本站 auth/callback/route.ts(第 7 篇原样复用)。删自己的留言:

await supabase.from("messages").delete().eq("id", id).eq("user_id", user.id)
// 加 user_id 条件 = 对象级鉴权(第 9 篇),RLS 再兜底

六、上线(第 13 篇)

  1. 推 GitHub → Vercel 导入 → 配环境变量。
  2. Supabase 执行上面的 schema.sql
  3. 配 GitHub OAuth + 域名 HTTPS。
  4. 打开手机访问,发一条留言,刷新看到它——你独立完成了第一个全栈应用 🎉

七、可复制的全栈工作流(记住这条线)

需求拆解(做 MVP)
   → 数据建模(建表 + RLS + 索引)
   → 页面(Server Component 读)
   → 写操作(Server Action + zod + 鉴权)
   → 安全自查(第 9 篇清单)
   → 测试(第 12 篇)
   → 部署监控(第 13 篇)

这套流程适用于绝大多数全栈项目:待办、博客、小电商、内部工具……换汤不换药。


总结

  • 综合实战 = 把 1-13 篇的零件按「需求→建模→页面→写→安全→部署」流水线组装。
  • 数据库层就做防御(CHECK / RLS / 索引),不把希望全押在接口。
  • Server Action 同时承载认证、校验、鉴权,是全栈写操作的核心形态。
  • 跑通留言板,你就证明了「能独立交付全栈应用」。

最后一篇,我们谈上线之后:维护、迭代,以及如何把这份能力变成作品集、接单与求职的资本。


练习:照这篇的七步,把「留言板」真正做出来并部署上线。做完它,你简历上就能写「独立全栈开发」——这是下一篇要聊的。

Comments

Sign in to leave a comment.