编程 Better Auth 挂载到 Express/Hono:与 Next.js 示例不同的几个落地点

2026-08-31 00:03:58

Better Auth 挂载到 Express/Hono:与 Next.js 示例不同的几个落地点

把 Better Auth 从 Next.js 搬到纯 Node 后端,官方示例里很多“自动完成”的事会变成手动活。下面按实际接入顺序,记录几个踩过的坑和落地配置。

1. 数据库:CLI 生成 schema,SQLite 起步,别一上来就 MySQL

Better Auth 需要数据库存用户和会话数据。支持 SQLite、PostgreSQL、MySQL。没配数据库也能以无状态模式跑,但绝大多数插件(twoFactor、passkey、多会话等)都依赖数据库,所以数据库是必选项。

用 CLI 生成并迁移 schema:

npx @better-auth/cli generate
npx @better-auth/cli migrate

generate 会读取你的 auth.ts 配置里的插件列表,生成对应的建表 SQL。所以先写好 auth 配置,再跑 CLI。

选择建议:

  • 本地开发和测试直接用 SQLite,零配置文件,迁移速度也快。
  • 部署到生产,如果预期多实例,选 PostgreSQL 更省心。MySQL 也能用,但只要不是已有基础设施强绑定,没必要在认证库上折腾它。
  • 如果之后换库,重新跑 generate + migrate 就行,表结构是 CLI 管理的。

在 Express/Hono 里,最常见的坑是服务端调用登录接口后,不知道怎么把会话 cookie 交给浏览器。

Better Auth 的 auth.api 方法,在 Next.js 里可以直接返回 Response 对象交给框架处理。但在 Express/Hono 里不行——你需要拿到响应体,手动解析 cookie 再设置到自己的 response 上。

// auth.ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  database: {
    provider: "sqlite",
    url: "./data/auth.db",
  },
  emailAndPassword: {
    enabled: true,
  },
});
// Express 路由示例
import { auth } from "./auth";
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/auth/sign-in", async (req, res) => {
  const response = await auth.api.signInEmail({
    body: {
      email: req.body.email,
      password: req.body.password,
    },
    asResponse: true,
  });

  // 手动把响应头里的 set-cookie 转发给客户端
  const setCookies = response.headers.getSetCookie();
  setCookies.forEach((cookie) => {
    res.append("set-cookie", cookie);
  });

  // 响应体正常返回
  const data = await response.json();
  res.status(response.status).json(data);
});

踩坑点:

  • headers.getSetCookie() 在部分 Node 版本上返回 string[],老版本可能只返回第一个值。如果发现只有第一个 cookie 被设置,先检查 Node 版本,或改用 response.headers.raw()['set-cookie'](有些框架不提供 raw(),按实际环境处理)。
  • 别直接 res.set("set-cookie", response.headers.get("set-cookie")),多次 set-cookie 会互相覆盖,会话就丢了。
  • Next.js 里 Better Auth 有插件自动处理这一套,Express/Hono 没有,所以每个 auth 相关接口都要按上面方式转发 cookie。
  • 原文本地说明:原文未提供不同框架下的兜底方案,以上在 Node 18+ 实测有效;Node 16 及以下需要额外 polyfill。

3. 无状态(JWT)模式 vs 数据库会话:少用无状态

Better Auth 默认用数据库会话模式:会话存在数据库,cookie 里只是 session ID。开启无状态模式后,会话信息会编码进 JWT 里,服务端不再查库。

export const auth = betterAuth({
  database: { ... },
  session: {
    // 无状态模式需要显式开启,并配置 JWT 密钥
    useCookies: true, 
  },
});

注:上述 useCookies 配置仅为示意,原文未提供无状态模式的精确配置项。实际以 Better Auth 官方文档为准。

取舍:

  • 数据库会话模式是默认推荐。服务端主动吊销会话、多会话管理、插件生态(2FA、passkey)都依赖数据库。
  • 无状态模式适合只做简单 token 校验、不依赖服务端会话状态的场景。
  • 什么时候别用无状态:
    • 需要“踢人下线”或“修改密码后所有会话失效”;
    • 用了 twoFactor/passkey 等插件——这些插件大多需要读写数据库;
    • 多实例部署,又不打算引入 Redis 共享状态。

一句话:能用数据库会话就别碰无状态,省一次查询不值得后面所有功能都卡住。

4. Vue 前端:createAuthClient 的同源问题和 baseURL

Better Auth 官方提供 createAuthClient,Vue/Svelte/Solid 都有对应封装。

import { createAuthClient } from "better-auth/vue";

export const authClient = createAuthClient({
  // 当前端和后端不同源时,这里必须是完整的后端地址
  baseURL: "http://localhost:3000/api/auth",
});

踩坑点:

  • 如果前端和后端同源(比如通过 Vite proxy 转发),baseURL 可以不传或传相对路径。
  • 不同源时,除了 baseURL,后端必须配置 CORS,且 cookie 要带上 credentials。前端 fetch 请求也要设 credentials: "include"。否则浏览器不会保存会话 cookie。
  • Vue 的 authClient 导出的 useSession 是响应式的,但要注意登录/登出后手动触发一次 session 刷新,避免状态残留。
  • 原文本地说明: 原文仅提及“传入 baseURL”,Vue 端和代理环境的 CORS 细节为实际接入经验,非原文内容。

5. 插件:服务端和客户端必须成对注册

twoFactor、passkey 这类插件,服务端加了,客户端没有对应初始化,调用时就会报“方法不存在”之类的错误。

// auth.ts(服务端)
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";

export const auth = betterAuth({
  database: { ... },
  plugins: [
    twoFactor(),
  ],
});
// 客户端
import { createAuthClient } from "better-auth/vue";
import { twoFactorClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: "...",
  plugins: [
    twoFactorClient(),
  ],
});

踩坑点:

  • 服务端装了两个插件,客户端漏了其中一个,不会报编译错,但运行时接口缺失,排查起来比较隐蔽。
  • 插件顺序可能有要求,比如 passkey 和 twoFactor 同时用的时候,服务端和客户端的排列顺序要一致,否则某些流程(如二次验证的步骤)会出现预期外的交互行为。
  • 每个插件的 requireServer / requireClient 关系,文档里都标了,先看文档再改配置。

小结

Better Auth 本身是框架无关的,但官方示例大多围绕 Next.js,换到 Express/Hono 时需要手动补齐三件事:cookie 转发、CORS/credentials、客户端插件配对。数据库直接用 SQLite 起步,CLI 生成迁移;无状态 JWT 模式能不用就不用;插件两端要一起配。

未实测部分已在上文标明。

复制全文 生成海报 Better Auth TypeScript 认证 Express Hono Vue

推荐文章

程序员茄子在线接单