Auth.js EdgeDB Adapter 完整实战指南:安装配置、ESDL 数据模型与数据库会话存储

📅 发布时间:2026/9/19 11:57:24
Auth.js EdgeDB Adapter 完整实战指南:安装配置、ESDL 数据模型与数据库会话存储
Auth.js EdgeDB Adapter 完整实战指南安装配置、ESDL 数据模型与数据库会话存储【免费下载链接】next-authAuthentication for the Web.项目地址: https://gitcode.com/gh_mirrors/ne/next-auth导读本篇指南以 packages/adapter-edgedb 官方适配器为主体讲解如何在 Auth.js / NextAuth.js 项目中接入 EdgeDB 数据库从依赖安装、环境变量、多框架配置到 EdgeDB CLI 初始化、default.esdl数据模型定义、迁移与查询构建器生成再到生产环境部署的完整链路。读完后你将掌握EdgeDBAdapter(client)的接入方式、它与auth/core中Adapter接口的对应关系以及底层每条 EdgeQL 语句的实际实现细节能够直接在自己的项目里落地一套以数据库为核心的认证会话存储方案。一、EdgeDB Adapter 是什么Auth.js 的数据库适配器机制定义在 packages/core/src/adapters.ts当你在AuthConfig中设置strategy: database时Auth.js 不再把会话放在 JWT 里而是通过Adapter接口把用户、账号、会话、验证令牌的读写操作映射到任意数据层。auth/edgedb-adapter正是这套机制在 EdgeDB 上的官方实现。在 packages/adapter-edgedb/src/index.ts 中可以看到其入口签名export function EdgeDBAdapter(client: Client): Adapter它接收一个 EdgeDB 官方 JS 客户端edgedb包的Client实例返回一个符合Adapter接口的对象。这种「工厂函数 复用外部 client」的模式与auth/core/adapters中推荐的官方适配器写法完全一致意味着你可以自行控制连接配置如 DSN、TLS、连接池并把同一个 client 复用到应用的其他数据访问逻辑中。二、安装依赖安装适配器本身以及 EdgeDB 客户端npm install edgedb auth/edgedb-adapter npm install edgedb/generate --save-devedgedb官方 TypeScript 客户端是适配器的 peer dependency。从 packages/adapter-edgedb/package.json 可以看到auth/edgedb-adapter声明的 peer 依赖为edgedb ^1.0.1且仅依赖auth/coreworkspace 引用本身零运行时数据库依赖非常轻量。edgedb/generate开发期依赖用于生成类型安全的 EdgeQL 查询构建器见下文「生成」一节。安装后包名auth/edgedb-adapter在 package.json 中登记其导出入口为./index.js./index.d.ts直接import { EdgeDBAdapter } from auth/edgedb-adapter即可使用。三、环境变量适配器本身不读环境变量但官方示例约定使用AUTH_EDGEDB_DSN存放 EdgeDB 连接串AUTH_EDGEDB_DSNedgedb://edgedb:p4ssw0rd10.0.0.1DSN连接字符串的标准格式为edgedb://username:passwordhostname:port。在本地开发时由于edgedb project init会把实例与当前目录「链接」客户端可以不传 DSN 自动连接在部署环境中则必须显式提供。四、各框架的接入配置4.1 Next.jsApp Router在项目根目录的auth.ts中创建客户端并传入适配器import NextAuth from next-auth import { EdgeDBAdapter } from auth/edgedb-adapter import { createClient } from edgedb const client createClient({ dsn: process.env.AUTH_EDGEDB_DSN }) export const { handlers, auth, signIn, signOut } NextAuth({ adapter: EdgeDBAdapter(client), providers: [], })4.2 Qwikimport { QwikAuth$ } from auth/qwik import { EdgeDBAdapter } from auth/edgedb-adapter import { createClient } from edgedb const client createClient({ dsn: import.meta.env.AUTH_EDGEDB_DSN }) export const { onRequest, useSession, useSignIn, useSignOut } QwikAuth$( () ({ providers: [], adapter: EdgeDBAdapter(client), }) )注意 Qwik 端使用import.meta.env.AUTH_EDGEDB_DSN读取环境变量这是 Vite 系构建工具的标准写法。4.3 SvelteKitimport { SvelteKitAuth } from auth/sveltekit import { EdgeDBAdapter } from auth/edgedb-adapter import { createClient } from edgedb const client createClient({ dsn: process.env.AUTH_EDGEDB_DSN }) export const { handle, signIn, signOut } SvelteKitAuth({ adapter: EdgeDBAdapter(client), providers: [], })4.4 Expressimport { ExpressAuth } from auth/express import { EdgeDBAdapter } from auth/edgedb-adapter import { createClient } from edgedb const app express() const client createClient({ dsn: process.env.AUTH_EDGEDB_DSN }) app.set(trust proxy, true) app.use( /auth/*, ExpressAuth({ providers: [], adapter: EdgeDBAdapter(client), }) )所有框架的接入模式完全一致createClient({ dsn })创建客户端 →EdgeDBAdapter(client)包装成适配器 → 传给框架对应的 Auth 初始化函数。其余部分provider 配置、回调等与使用其他数据库适配器时的写法相同。五、适配器源码实现深度解析适配器的完整实现集中在 packages/adapter-edgedb/src/index.ts所有方法均使用 EdgeDB 的原生 EdgeQL 通过client.querySingle/client.queryRequiredSingle/client.execute执行。以下按Adapter接口的分组逐一拆解。5.1 用户管理User创建用户createUser源码 L29-L59async createUser({ email, emailVerified, name, image }) { return await client.queryRequiredSingle( with image : optional str$image, name : optional str$name, emailVerified : optional str$emailVerified select ( insert User { email: str$email, emailVerified: datetimeemailVerified, name: name, image: image, } ) { id, email, emailVerified, name, image } , { email, emailVerified: emailVerified new Date(emailVerified).toISOString(), name, image, } ) }实现要点所有可选字段image、name、emailVerified在 EdgeQL 中声明为optional str避免传入undefined时类型报错JS 侧的Date在传入前被转换为 ISO 字符串EdgeQL 侧再用datetime显式转型为数据库的datetime类型使用insert ... select结构插入后立即以投影形式返回完整用户对象。查询用户getUser(id)、getUserByEmail(email)分别按User.iduuid$id和User.emailstr$email过滤返回AdapterUser或null见 源码 L60-L87。按账号查用户getUserByAccount源码 L88-L106是数据库适配器实现的关键技巧先用子查询with account : (select Account filter ...)找到账号再通过反向链接account.user取出关联的用户一条 EdgeQL 完成关联查询无需两次往返。更新用户updateUser源码 L107-L141使用 EdgeQL 的合并运算符??实现「传入才更新未传保留原值」的语义set { email : email ?? .email, emailVerified : datetimeemailVerified ?? .emailVerified, image : image ?? .image, name : name ?? .name, }删除用户deleteUser源码 L142-L144直接执行delete User filter .id uuid$id;。由于 ESDL schema 中Account、Session都声明了on target delete delete source删除 User 时关联的账号与会话会被级联删除这是数据一致性由数据库层保证的典型设计。5.2 账号关联AccountlinkAccount源码 L145-L200插入一条Account记录并把user链接指向按userId查到的Userinsert Account { type : str$type, provider : str$provider, providerAccountId : str$providerAccountId, ... user : ( select User filter .id uuiduserId ) }这里有一个值得注意的细节expires_at在 OAuth 返回中通常是秒级数字适配器会先String(expires_at)转为字符串再在 EdgeQL 中int64转回整数源码 L193。unlinkAccount源码 L201-L211则按providerAccountId provider组合条件删除对应 Account。5.3 会话管理SessioncreateSession源码 L212-L231插入Session并返回{ expires, sessionToken, userId }。getSessionAndUser源码 L232-L268是数据库会话策略的核心一次查询同时拉取会话和其关联用户EdgeDB 的链接投影天然支持嵌套返回{ user, session }若任一部分缺失则返回null。这与auth/core中「支持联表查询的数据库应减少往返次数」的建议完全吻合。updateSession源码 L269-L300同样用??保留旧值并且对用户链接使用了assert_exists(user ?? .user)保证不会产生悬空引用。deleteSession按sessionToken删除。5.4 验证令牌VerificationTokencreateVerificationToken源码 L307-L327插入令牌记录useVerificationToken源码 L328-L348则采用「查询即删除」的原子语义——用delete ... filter .token ... and .identifier ...一次性取回并消费令牌天然保证令牌只能使用一次这正是auth/core对useVerificationToken的契约要求。5.5 适配器的自动化测试packages/adapter-edgedb/test/index.test.ts 通过runBasicTests来自 packages/utils/adapter.ts对适配器跑一套跨数据库的统一基础测试const client createClient() runBasicTests({ adapter: EdgeDBAdapter(client), db: { connect: async () { /* 清空 User/Account/Session/VerificationToken 四张表 */ }, disconnect: async () { /* 同上清理 */ }, user: async (id) client.querySingle(select User {...} filter .id uuid$id), account: async ({ providerAccountId, provider }) { /* ... */ }, session: async (sessionToken) { /* ... */ }, verificationToken: async ({ token, identifier }) { /* ... */ }, }, })这意味着只要你的 EdgeDB 实例可用直接运行pnpm test对应 vitest 配置即可验证适配器的用户增删改查、账号链接、会话与令牌全流程是否符合 Auth.js 契约。六、EdgeDB CLI 安装与项目初始化6.1 安装 CLILinux / macOScurl --proto https --tlsv1.2 -sSf https://sh.edgedb.com | shWindowsPowerShelliwr https://ps1.edgedb.com -useb | iex安装后用edgedb --version验证。如果提示Command not found通常需要重新打开一个终端窗口让 PATH 生效。6.2 初始化项目在应用根目录执行edgedb project init该命令会启动一个本地 EdgeDB 实例并把当前目录与实例「链接」。此后只要你在该目录下运行 CLI 命令或使用客户端库都能自动发现并连接这个实例无需额外配置连接参数——这正是上文「本地开发可不传 DSN」的原因。七、数据模型替换 default.esdl初始化后用以下内容替换自动生成的dbschema/default.esdl。这是整个适配器能够正常工作的基石——它定义了 Auth.js 所需的四类模型module default { type User { property name - str; required property email - str { constraint exclusive; } property emailVerified - datetime; property image - str; multi link accounts : .user[is Account]; multi link sessions : .user[is Session]; property createdAt - datetime { default : datetime_current(); }; } type Account { required property userId : .user.id; required property type - str; required property provider - str; required property providerAccountId - str { constraint exclusive; }; property refresh_token - str; property access_token - str; property expires_at - int64; property token_type - str; property scope - str; property id_token - str; property session_state - str; required link user - User { on target delete delete source; }; property createdAt - datetime { default : datetime_current(); }; constraint exclusive on ((.provider, .providerAccountId)) } type Session { required property sessionToken - str { constraint exclusive; } required property userId : .user.id; required property expires - datetime; required link user - User { on target delete delete source; }; property createdAt - datetime { default : datetime_current(); }; } type VerificationToken { required property identifier - str; required property token - str { constraint exclusive; } required property expires - datetime; property createdAt - datetime { default : datetime_current(); }; constraint exclusive on ((.identifier, .token)) } } # Disable the application of access policies within access policies # themselves. This behavior will become the default in EdgeDB 3.0. # See: https://www.edgedb.com/docs/reference/ddl/access_policies#nonrecursive using future nonrecursive_access_policies;几个需要重点理解的设计反向链接与计算属性User.accounts/User.sessions使用.user[is Account]反向链接语法定义Account.userId、Session.userId是通过.user.id计算的属性查询时无需额外存储与auth/core中AdapterSession.userId字段一一对应。级联删除Account与Session的user链接都声明了on target delete delete source删除用户时其账号与会话自动清除对应源码deleteUser的级联行为。唯一性约束User.email、Account.providerAccountId、Session.sessionToken、VerificationToken.token各自exclusive同时用constraint exclusive on ((.provider, .providerAccountId))和constraint exclusive on ((.identifier, .token))定义了复合唯一约束防止同一 OAuth 账号或同一验证令牌被重复绑定/使用。时间字段所有模型补充了createdAt - datetime { default : datetime_current() }作为审计字段emailVerified、expires使用 EdgeDB 的datetime类型与源码中datetime转型对应。末尾的using future nonrecursive_access_policies;是 EdgeDB 2.x 兼容 3.0 默认行为的声明用于避免访问策略内部递归应用保留该行即可。模型命名与字段与auth/core的Adapter契约packages/core/src/adapters.ts 中AdapterUser/AdapterAccount/AdapterSession/VerificationToken严格对齐因此适配器无需额外的字段映射逻辑。八、迁移创建并应用生成迁移文件edgedb migration create该命令对比 schema 与数据库当前状态生成迁移脚本。应用迁移edgedb migrate建议在每次修改default.esdl后重复这两步保持数据库与 schema 同步。九、生成类型安全的查询构建器为了让应用代码以「代码优先」的方式编写完全类型化的 EdgeQL需要生成查询构建器npx edgedb/generate edgeql-js生成后即可写出带完整类型推导的查询const query e.select(e.User, () ({ id: true, email: true, emailVerified: true, name: true, image: true, filter_single: { email: johndoeexample.com }, })) return await query.run(client)注意适配器源码本身使用原生 EdgeQL 字符串queryRequiredSingle等并不依赖生成的查询构建器查询构建器是给应用业务代码使用的类型安全工具两者互不冲突。从 packages/adapter-edgedb/tsconfig.json 可以看出适配器包仅编译src目录构建产物为 ESMtype: module。十、部署到生产环境10.1 部署 EdgeDB 实例先在云厂商部署一个 EdgeDB 实例官方支持的部署途径包括 AWS、Google Cloud、Azure、DigitalOcean、Fly.io 以及 Docker云厂商无关方案。10.2 获取 DSNDSN 即连接字符串格式为edgedb://username:passwordhostname:port具体获取方式取决于云厂商的控制台/CLI。10.3 设置环境变量在.env中写入AUTH_EDGEDB_DSNedgedb://johndoe:supersecuremyhost.com:420确保应用运行环境如 Vercel、Render、自有服务器也注入该变量。10.4 对远程实例应用迁移用 DSN 指向远程实例执行迁移edgedb migrate --dsn your-instance-dsn10.5 配置 prebuild 脚本在package.json中添加prebuild钩子宿主平台构建初始化时会先触发它读取EDGEDB_DSN环境变量连接数据库并生成查询构建器再开始编译项目scripts: { dev: next dev, build: next build, start: next start, lint: next lint, prebuild: npx edgedb/generate edgeql-js },注意prebuild读取的是EDGEDB_DSNEdgeDB 工具的默认变量名而应用运行时代码读取的是AUTH_EDGEDB_DSN两者在部署平台上都应配置为同一个 DSN 值。十一、小结auth/edgedb-adapter是一个结构清晰、契约完整的官方数据库适配器接入简单createClient({ dsn })EdgeDBAdapter(client)两行代码即可接入 Next.js、Qwik、SvelteKit、Express 等任意支持 Auth.js 的框架实现完整源码 覆盖Adapter接口的用户、账号、会话、验证令牌全部方法并在单条 EdgeQL 内完成关联查询、级联删除、原子消费令牌等操作可测试测试用例 通过统一的runBasicTests套件保证与 Auth.js 核心契约的兼容性配套完善仓库内的 配套文档 提供了从 CLI 安装、schema 定义到云端部署的端到端指引配合本文的源码级解析足以支撑你在生产项目中稳定落地 EdgeDB 认证存储方案。【免费下载链接】next-authAuthentication for the Web.项目地址: https://gitcode.com/gh_mirrors/ne/next-auth创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考