走向全栈 —— Neon + Auth
学习目标
到本节课结束,学员应当能够:
- 通过提示词开一个生产级 Postgres 数据库 + 一套 Auth 系统 —— Neon MCP 创建 DB,Drizzle 定义 schema,Neon Auth 接通 Google 登录,全部在一段对话里发生。
- 识别并落地 OAuth 同意页这一步——这是 AI 唯一帮不了你的步骤——做到不慌、不跳过。
- 在一节 2 小时的课里端到端交付一个全栈功能(登录用户在你网站上留言),即便你写过零行后端代码。
核心主题
- 当 AI 来写整套 stack 时,"走向全栈"到底意味着什么:数据库、Auth 层、server action、UI——一次性全打齐。
- Drizzle ORM 作为 AI 能推理的、类型安全的 schema 定义。
- Neon Auth —— 它为什么存在;为什么对新手来说,它是从"没有登录"走到"用户可以登录"最快的路径。
- 跨三套环境(local、preview、production)的环境变量管道;为什么"AI 替你做这件事"是超能力。
- OAuth 信任探戈:为什么 Google 必须人来同意;callback URL 必须严格匹配。
工具 / 技术栈
| 工具 | 本周角色 |
|---|---|
| Neon Postgres(通过 MCP) | Serverless Postgres;AI 通过 Neon MCP 开。 |
| Neon Auth | 开箱即用的 Auth,带 Google provider;AI 接线。 |
| Drizzle ORM | 类型安全的 schema,AI 读得懂、能扩展。 |
| Vercel CLI | 在 Neon → 本地 → 生产之间同步环境变量。 |
| Google Cloud Console | 唯一一个需要人打开的 OAuth 同意页所在地。 |
课堂计划
| 时间 | 活动 |
|---|---|
| 0 – 15 分钟 | 回顾与 Check-in。 这周谁的聊天组件挂了?快速分诊。另外:有没有人已经被陌生人用过 AI 克隆? |
| 15 – 40 分钟 | 概念讲解。 Postgres 是什么——讲到新手够用就行。ORM 干什么、不干什么。Auth provider 和 callback URL。为什么"环境变量管道"是 20 年的老痛点,而我们现在可以跳过它。 |
| 40 – 75 分钟 | 现场演示。 讲师在自己的站上加一个完整的留言簿——开 Neon、配 Drizzle schema、接 Google 登录、保护 UI——18 分钟的提示词搞定。全班看完整流程。 |
| 75 – 105 分钟 | 动手 Lab。 学员在自己站上复现。OAuth 这一步是唯一真正"人参与"的瞬间——预演两遍,没人会慌。 |
| 105 – 120 分钟 | Q&A + 收尾。 最常见的失败:callback URL 不匹配。我们当场调一个。 |
动手 Lab
任务。 下课时,你的站会有一个留言簿。访客点 Sign in with Google,写一条消息,立即就能在 About 页看到(带名字 + 头像)。刷新——消息还在。这是一个真正的全栈功能:数据库、Auth、server action、UI。
阶段 1 —— 开数据库
请给这个项目开一个新的 Neon Postgres 数据库。用 Neon MCP 服务器。项目名
my-portfolio-db。建好以后:
- 把 pooled 连接串作为
DATABASE_URL存到我的.env.local。- 通过 Vercel CLI 把同一个
DATABASE_URL加到我的 Vercel 生产环境 和 预览环境。- 在我的 Next.js 项目里安装 Drizzle ORM(
drizzle-orm、drizzle-kit、pg)。- 创建
drizzle.config.ts和db/schema.ts,准备 好让我下一步加表。先别建任何表——我下一步要做这件事。回报告时告诉我数据库的 region 和你设了哪些环境变量。
- Cursor 确认 Neon 项目已创建,并打印出 region。
- 你的
.env.local里有DATABASE_URL=postgresql://...这一行。 - 跑
vercel env ls(Cursor 会跑)显示production和preview都有DATABASE_URL。 drizzle.config.ts和db/schema.ts都存在。
阶段 2 —— 定义 schema + 跑迁移
现在用 Drizzle 定义留言簿的 schema。我要两张表:
users—— 一张极简的 profile 表,让 Neon Auth 来 own。列:id(主键,text)、name(text)、image(text,可空)、created_at(timestamp 默认 now)。guestbook_messages—— 列:id(主键,serial)、user_id(text,外键 →users.id,cascade on delete)、body(text,not null,1–500 字符)、created_at(timestamp 默认 now)。用
drizzle-kit generate生成迁移 SQL,先把它在聊天里给我看一下,然后用drizzle-kit push推到 Neon 数据库上。完成后对两张表都跑一个等价于SELECT 1的查询,确认两张表都存在。
- Cursor 在执行前把迁移 SQL 给你看了。
- push 成功,没报错。
- 验证查询对两张表都返回 0 行。
阶段 3 —— 接通 Neon Auth(人工环节就在中间)
在这个项目里安装 Neon Auth。配成只用 Google 作为登录 provider,并把已认证用户写到我刚才定义的
users表里。加一个Sign in with Google按钮组件,我能放到任何地方。先别填 Google 凭据——那个等会儿要我给你。告诉我具体要从 Google Cloud Console 取哪些值。
Cursor 会让你打开 Google Cloud Console。在里面:
- 新建一个项目,叫
my-portfolio-auth(或者用已有的)。 - 如果提示,启用 Google+ API / People API。
- 进 APIs & Services → OAuth consent screen。选 External,应用名填
My Portfolio,user support email 填你的邮箱,保存。 - 进 Credentials → Create Credentials → OAuth client ID。Application type 选 Web application。把 Cursor 刚才打印出来的 Authorised redirect URIs 加进去(会有两条——一条 localhost,一条 production)。
- 复制生成的 Client ID 和 Client secret。
Why manual: Google 要求人接受 OAuth 条款,并手动把 redirect URI 粘进它的网页控制台。任何 API、CLI、MCP、skill 都做不了——是 Google 故意这么设计的。
这里是 Google OAuth 凭据:
- Client ID:[粘贴]
- Client secret:[粘贴]
请:
- 通过 CLI 把
GOOGLE_CLIENT_ID和GOOGLE_CLIENT_SECRET加到.env.local和 Vercel(production + preview)。- 接到 Neon Auth 的 Google provider 配置里。
- 同时生成一个强随机的
AUTH_SECRET,三个环境里都设上。- 重启 dev server,确认
http://localhost:3000上的 Sign in with Google 按钮现在能端到端启动 Google 流程。
- 在 localhost 点 Sign in with Google 跳到 Google 同意页;登录后回到你的站,已是登录态。
users表里出现一行带你 Google 名字 + 邮箱的记录。- 任何 commit 进去的文件里都没有
GOOGLE_CLIENT_SECRET或AUTH_SECRET字样(git status确认.env.local是 ignored)。
登录回跳报
Error 400: redirect_uri_mismatch。请告诉我 Neon Auth 当前请求的具体 redirect URI 是什么,跟 Google Cloud Console 里登记的对一下,引导我在 Google 控制台里修。我负责复制粘贴。
阶段 4 —— 做留言簿 UI + server action
现在在我的
/about页上做留言簿功能:
- 用户 未 登录:在消息列表上方显示一个小的 Sign in with Google to leave a message 按钮。
- 用户 已 登录:显示一个紧凑的表单:textarea(最多 500 字符,可见的字符计数)、Post 按钮、一个小标签 "posting as {user.name}"。
- 表单(或按钮)下方按"最新在前"渲染所有消息。每条显示发帖人头像、名字、相对时间("3 minutes ago")。我自己发的消息有一条淡淡的 accent 边框。
- 用 Next.js server action 做 post。在 server 端校验长度 + 去掉首尾空白。返回一次乐观更新,让消息瞬间出现。
- post 成功后 revalidate
/about页面,让别人来访问也能看到新消息。用站点已有的 Tailwind tokens 和强调色。分两条 commit:一条是 server action,一条是 UI。
- 在 localhost 未登录态下,看到登录按钮。
- 登录后表单出现。post "Hello from the future"。
- 消息瞬间出现在表单上方,带你的头像和 "just now"。
- 刷新。还在。
- 开一个无痕窗口,用另一个 Google 账号登录,post 一条。回到原窗口刷新——能看到对方的消息。
阶段 5 —— 上线
部署到生产。确认 Vercel prod 环境变量都设了,把 commit 推上去,等部署完。然后:打开生产 URL,用 Google 登录,发一条消息。确认消息出现在线上站。再用一个无痕窗口看一下 —— 消息还在(证明 Neon 在生产环境也通了)。
- 生产部署成功。
- 线上站点显示留言簿。
- 你在第 11 步本地发的那条消息 不 在生产上(数据不同)。在生产新发的一条立即出现。
- 你的 Neon 控制台显示
users和guestbook_messages两张表里都有行。
任何时候你看到 Cursor 准备把 GOOGLE_CLIENT_SECRET = "..." 写进源代码文件,立即拦下:"别把 secret 放在源代码里。只能进 .env.local,并通过 Vercel CLI 设置。" 这是要练成肌肉记忆的——很多真实的生产泄露事件,起点都是 IDE 自动补全把 secret 补进了错误的文件。
本周作业
做 / 交付。
- 你线上站点上一个能用的留言簿:用 Google 登录、发消息、刷新和换浏览器都能看到。
要求。
- Neon Postgres 是唯一真实数据源。不准用内存数组。
- Drizzle schema 进 repo。
- Google OAuth callback URL 在
localhost和生产环境都能跑通。 - 提交时线上留言簿至少有 3 条消息(让同学帮你 post 一些,作为真实数据)。
- 一张 Neon 控制台截图,显示
users和guestbook_messages两张表都有行。
提交。 第 5 周开课前,把线上 URL + Neon 截图发到 Slack。
资源
| 文档 | 视频 | 仓库 |
|---|---|---|
| Neon —— MCP server + CLI | 讲师 demo:"一段对话搞定全栈" | drizzle-team/drizzle-orm —— 示例 |
| Neon Auth —— Next.js 集成 | neondatabase/neon —— docs repo | |
| Drizzle ORM —— schema 基础 + 迁移 | ||
| Vercel 环境变量 —— 按环境管理 |
真实世界应用
这是整学期的拐点周。 第 4 周之前你只是在发带 LLM 调用的静态站。第 4 周之后你在发 真正的产品 —— 登录用户把状态持久化到真实数据库。今天学的这套 stack(Next.js + Neon + Drizzle + 一个 Auth provider),就是 2026 年大多数 AI 创业公司的生产栈。你现在可以毫无伪装焦虑地在 LinkedIn 上写 full-stack developer 了。
扫你 LinkedIn 的招聘官在 "guestbook with OAuth + Postgres + Drizzle, deployed to Vercel" 这一行停下来,心想:"这个人能做出功能。" —— 这正是你想要的信号。
踩坑提醒 & 小贴士
- "
drizzle-kit push让我确认一个破坏性变更。" 读一下 Cursor 输出里的 diff。如果是缩列、删表,停下来问 Cursor:"为什么这次迁移要删 X?有没有更安全的路径?" 在课堂数据库里通常无所谓,但读迁移 diff 的习惯本身才是重点。 - "OAuth 在 localhost 可以,生产挂了。" Google Cloud Console 里 callback URL 是按环境分的。你需要 两条 都注册:
localhost:3000/api/auth/callback/google和your-project.vercel.app/api/auth/callback/google。让 Cursor 把两条都列出来,然后在 Google 控制台里修。 - "刷新后我的 session 没了。" auth cookie 没设到生产域名上。问 Cursor:"看一下我 Neon Auth 的 cookie 配置——为什么 session 在线上没保持?"
- "我发了消息但没显示。" server action 的 revalidate 漏了。说:"在 server action insert 成功后加上
revalidatePath('/about')。" - "我的 Neon 免费额度到 80% 了。" 你存了一堆带表情包的消息,或者忘了清测试表。问 Cursor:"显示我 Neon 的存储用量,帮我删掉所有没用的测试表。"
把浏览器地址栏里的 完整 错误粘到 Cursor。95% 的 OAuth 错误都编码在那串 query string 里——Cursor 能解码并修。