my-first-ai-web_学习记录05——NextAuth Adapter 存储用户信息

day008 的核心问题是:上一节的登录状态只存在于 Session Cookie 中,虽然 session.user 能拿到 GitHub 用户信息,但数据库里没有一个稳定的用户实体。

开始学习之前让我们带着这几个问题一起往下看:

  • 为什么不能只建一张 User 表,还要额外建 Account 和 Session?

  • 为什么 sessionToken 要唯一?

  • 为什么 expiresDateTime,而 Account 的 expires_atInt

  • 为什么 Session 也需要 userId 外键?

这一节要引入:

markdown 复制代码
GitHub OAuth
    ↓
NextAuth
    ↓
Prisma Adapter
    ↓
PostgreSQL

先把几层关系重新捋一遍

arduino 复制代码
PostgreSQL
  ↓
真正存储数据的数据库服务

Prisma Schema
  ↓
用 Prisma 描述数据库表结构的文件

Prisma Client
  ↓
根据 Schema 生成的 TypeScript 数据库操作 API

src/db/db.ts 中的 db
  ↓
Prisma Client 的一个实例

PrismaAdapter(db)
  ↓
把这个 Prisma Client 交给 NextAuth 使用

src/db/db.ts

arduino 复制代码
export const db = globalForPrisma.prisma ?? createPrismaClient()

这个 db 可以理解成:

arduino 复制代码
一个已经连接 PostgreSQL、可以执行查询的 Prisma Client 对象

例如项目中的代码:

scss 复制代码
db.doc.findMany()
db.doc.create(...)
db.doc.update(...)

这些都不是直接调用 PostgreSQL,而是调用 Prisma Client。Prisma Client 在底层把它们转换成 SQL,再通过 PostgreSQL 驱动执行。

Prisma 到底是什么

Prisma 不是 PostgreSQL,也不是数据库本身。

它是一套 Node.js / TypeScript 数据库工具,主要包括:

arduino 复制代码
Prisma Schema
  → 定义模型

Prisma Client
  → 在 TypeScript 中操作数据

Prisma CLI
  → generate、db push、migrate 等命令

例如:

kotlin 复制代码
model Doc {
  id      Int    @id @default(autoincrement())
  title   String
  content String
}

执行:

arduino 复制代码
npm run db:push

大致过程是:

bash 复制代码
读取 prisma/schema.prisma
  ↓
连接 DATABASE_URL 指向的 PostgreSQL
  ↓
创建或调整数据库中的表结构

执行:

arduino 复制代码
npm run db:generate

则是:

arduino 复制代码
读取 prisma/schema.prisma
  ↓
生成 TypeScript Prisma Client

所以:

perl 复制代码
db push      → 同步数据库表结构
db generate  → 生成代码 API

这两个动作不是一回事。

NextAuth 是什么

NextAuth 也是一个 npm 包,但它解决的是另一个领域:

复制代码
NextAuth
  → 登录、OAuth、Session、Cookie、Provider、授权流程

它不是数据库工具,也不是 GitHub 本身。

可以这样分工:

arduino 复制代码
GitHub
  → 外部身份提供商

NextAuth
  → 认证流程管理器

Prisma Adapter
  → NextAuth 与 Prisma Client 之间的翻译层

Prisma Client
  → TypeScript 数据库操作 API

PostgreSQL
  → 最终存储数据

完整关系:

scss 复制代码
GitHub OAuth
    ↓
NextAuth
    ↓
PrismaAdapter(db)
    ↓
Prisma Client
    ↓
PostgreSQL

Adapter 是什么

Adapter 可以理解成 NextAuth 和数据库之间的"翻译层"。

NextAuth 需要完成这些操作:

复制代码
查找用户
创建用户
保存 GitHub 账号关联
创建 Session
查询 Session
删除 Session
保存邮箱验证码

但 NextAuth 不知道你使用 PostgreSQL、MySQL 还是 MongoDB,也不知道你的 ORM 是 Prisma 还是 Drizzle。

所以它定义了一套 Adapter 接口:

arduino 复制代码
NextAuth
  ↓ 调用统一的 Adapter 方法
Prisma Adapter
  ↓ 翻译成 Prisma API
Prisma Client
  ↓
PostgreSQL

例如,用户第一次用 GitHub 登录时,NextAuth 可能需要:

scss 复制代码
adapter.getUserByAccount(...)
adapter.createUser(...)
adapter.linkAccount(...)
adapter.createSession(...)

@auth/prisma-adapter 负责把这些操作翻译成 Prisma 查询。

它不是把整个 session.user 随便存进数据库,而是按照认证领域的数据模型拆成多张表。

Adapter 为什么需要 db

NextAuth 只知道自己需要调用一些"存储用户"的操作,例如:

复制代码
createUser
getUser
getUserByAccount
linkAccount
createSession
getSessionAndUser
deleteSession

但它不知道你项目使用什么数据库。

PrismaAdapter(db) 做的事情就是告诉 NextAuth:

arduino 复制代码
如果你需要 createUser,
我会用 Prisma Client 的方式帮你执行。

如果你需要 getUserByAccount,
我也会把它翻译成 Prisma 查询。

所以它不是"Adapter 解释 db 给 Prisma",而是:

Adapter 使用你提供的 Prisma Client,把 NextAuth 的标准存储操作翻译成 Prisma 查询。

例如概念上可能类似:

vbnet 复制代码
NextAuth:
  createUser(user)

PrismaAdapter:
  db.user.create({ data: user })

Prisma:
  INSERT INTO "User" ...

PostgreSQL:
  真正写入记录

为什么需要多张表

标准 Prisma Adapter 通常需要这些模型:

sql 复制代码
User
  用户的基本身份信息

Account
  用户和 GitHub 等登录平台的关联

Session
  当前登录会话

VerificationToken
  邮箱登录时的一次性验证码或验证链接

关系大致是:

sql 复制代码
User 1 ─── N Account
User 1 ─── N Session

同一个用户未来可能绑定多个登录方式:

sql 复制代码
一个 User
  ├── GitHub Account
  ├── 微信 Account
  └── 邮箱登录记录

所以不能只把 GitHub 返回的用户对象直接塞进一个表里。

登录后的数据库变化

第一次 GitHub 登录,数据库大致会发生:

markdown 复制代码
1. 查询 Account:这个 GitHub 账号以前登录过吗?
2. 没有找到:
   - 创建 User
   - 创建 Account,记录 GitHub 账号关联
3. 创建 Session
4. 浏览器仍然拿到 Session Cookie

再次登录时通常是:

markdown 复制代码
1. 根据 GitHub provider 和 providerAccountId 找到 Account
2. 通过 Account 找到 User
3. 创建或更新 Session
4. 不重复创建 User

这里要区分:

sql 复制代码
GitHub Access Token
  → 第三方授权凭证

User
  → 你的系统中的用户实体

Session
  → 你的系统中的一次登录会话

现在你能试着回答文章一开始的几个问题了吧?

相关推荐
不是株2 天前
零到全栈(网页从哪来,服务器怎么架)
全栈
Ticnix2 天前
42 天 71 次提交之后,我重新看了一遍自己的架构决策
python·agent·全栈
LEE3 天前
前端转型全栈 03:接口失败也返回 200,OpenAPI 契约与错误码怎么定
前端·后端·全栈
SL_staff3 天前
MQTT Topic权限越界:JVS-IOT中系统Topic与自定义Topic的权责边界与验证实践
java·物联网·全栈
SL_staff4 天前
财务系统慎用低代码?从数据模型闭环看合规落地的技术实践
java·低代码·全栈
老王以为4 天前
走进 AI Agent 第四篇(上):知识获取管道——RAG 基础
前端·人工智能·全栈
名字还没想好☜4 天前
React 文件上传实战:预览 URL 回收、多文件逐个进度与拖拽放置区踩坑
前端·react·next.js
右耳朵猫AI4 天前
Web前端周刊2026W38 | React 19.3 发布、StyleX 深潜、jsdom 30.1 提速
前端·javascript·react.js·typescript·node.js
溪语流沙4 天前
Django + Vue电商项目第001讲:开篇|注册登录加增删改查,那不是电商
redis·python·mysql·docker·typescript·django·vue
掰头战士5 天前
多重影分身!恨不得把一个agent掰成两个? 还真能干!
typescript·llm·agent