You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cloudflare+Express+D1环境下Better Auth服务端方法使用及结构疑问

问题背景

我在部署了Cloudflare D1绑定的Express.js/Node.js应用中使用Better Auth。由于D1数据库绑定(env.DB)仅在请求上下文内可用,我的认证设置通过Cloudflare Worker风格的fetch() handler实现,当前auth.ts代码如下:

import { betterAuth } from "better-auth";

export default {
    async fetch(request, env) {
        const auth = betterAuth({
            database: env.DB, // D1 binding, auto-detected
            /// Rest of better auth config
        });

        return auth.handler(request);
    },
} satisfies ExportedHandler<{ DB: D1Database }>;

我需要直接调用Better Auth的服务端方法(如signUpEmail()),相关代码如下:

import auth from "../../infrastructure/auth.ts";

export async function signUpWithEmail(
    pseudo: string,
    email: string,
    password: string
) {
    if (!pseudo || !email || !password) {
        throw new Error("Pseudo, email and password are required");
    }

    try {
        const result = await auth.api.signUpEmail({ // 这里出现问题
            body: {
                name: pseudo,
                email: email,
                password: password,
                display_pseudo: pseudo,
            },
        });

    } catch (error) {
        console.error(`Signup error: ${error}`);
        throw error;
    }
}

但导入的auth对象仅包含fetch()方法,导致auth.api未定义/出现TypeScript报错。

约束条件

  • 需通过env.DB访问Better Auth(因Cloudflare D1);
  • env.DB仅在请求上下文内可用;
  • 希望在服务中直接使用Better Auth的signUpEmail、signInEmail等方法。

我的问题

  1. 当前auth.ts结构是否适用于Cloudflare+Express+D1配置?
  2. 在此架构下,暴露/使用Better Auth服务端方法的推荐方式是什么?

解决方案与最佳实践

1. 当前auth.ts结构是否适用?

不适用。你现在的auth.ts导出的是Cloudflare Worker风格的handler对象,而非Better Auth实例本身。这种结构只能处理HTTP请求转发,无法直接暴露服务端API方法,且每次请求都会重新初始化Better Auth实例,既浪费资源也不符合Express应用的使用习惯。

2. 推荐的实现方式

由于env.DB仅能在请求上下文获取,需调整auth.ts结构,将Better Auth的初始化逻辑封装为可接收env参数的工厂函数,同时通过Express中间件注入实例,或提供上下文获取方法供服务层调用。

方案一:工厂函数+Express中间件注入

修改auth.ts,导出创建Better Auth实例的工厂函数,同时提供Express兼容的中间件:

import { betterAuth, type BetterAuth } from "better-auth";
import type { D1Database } from "@cloudflare/workers-types";

// 工厂函数:接收env返回Better Auth实例
export function createAuth(env: { DB: D1Database }): BetterAuth {
  return betterAuth({
    database: env.DB,
    // 其他Better Auth配置
  });
}

// Express中间件:注入auth实例到请求对象
export function authMiddleware(env: { DB: D1Database }) {
  const auth = createAuth(env);
  return (req, res, next) => {
    // 挂载auth实例到req,供后续路由/服务使用
    (req as any).auth = auth;
    next();
  };
}

// 保留Worker风格的fetch handler(兼容Worker部署)
export default {
  async fetch(request, env) {
    const auth = createAuth(env);
    return auth.handler(request);
  },
} satisfies ExportedHandler<{ DB: D1Database }>;

在Express应用中配置中间件,并在服务层通过请求上下文获取实例:

// Express入口文件
import express from "express";
import { authMiddleware } from "./infrastructure/auth.ts";
import { signUpWithEmail } from "./services/authService.ts";

const app = express();
// 注入env(Cloudflare部署自动传入,本地开发可模拟)
app.use(authMiddleware({ DB: process.env.DB as D1Database }));

// 示例注册路由
app.post("/signup", async (req, res) => {
  try {
    const { pseudo, email, password } = req.body;
    const result = await signUpWithEmail(req.auth, pseudo, email, password);
    res.json(result);
  } catch (err) {
    res.status(400).json({ error: (err as Error).message });
  }
});

修改服务层函数,接收auth实例作为参数:

import type { BetterAuth } from "better-auth";

export async function signUpWithEmail(
  auth: BetterAuth,
  pseudo: string,
  email: string,
  password: string
) {
  if (!pseudo || !email || !password) {
    throw new Error("Pseudo, email and password are required");
  }

  try {
    const result = await auth.api.signUpEmail({
      body: {
        name: pseudo,
        email,
        password,
        display_pseudo: pseudo,
      },
    });
    return result;
  } catch (error) {
    console.error(`Signup error: ${error}`);
    throw error;
  }
}

方案二:请求上下文容器(适用于复杂架构)

如果服务层不想直接依赖Express的req对象,可封装异步上下文容器,在请求开始时注入auth实例:

// context.ts
import { AsyncLocalStorage } from "async_hooks";
import type { BetterAuth } from "better-auth";

const authContext = new AsyncLocalStorage<BetterAuth>();

export function setAuthContext(auth: BetterAuth) {
  return authContext.run(auth, () => {});
}

export function getAuthContext(): BetterAuth {
  const auth = authContext.getStore();
  if (!auth) throw new Error("Auth context not initialized");
  return auth;
}

修改authMiddleware注入上下文:

// auth.ts
import { setAuthContext } from "./context.ts";

export function authMiddleware(env: { DB: D1Database }) {
  const auth = createAuth(env);
  return (req, res, next) => {
    setAuthContext(auth);
    next();
  };
}

服务层直接通过上下文获取auth实例:

// authService.ts
import { getAuthContext } from "../context.ts";

export async function signUpWithEmail(
  pseudo: string,
  email: string,
  password: string
) {
  const auth = getAuthContext();
  // 后续逻辑同方案一
}

关键注意事项

  • 禁止全局初始化Better Auth实例:env.DB仅在请求上下文可用,全局初始化会导致无法获取数据库连接。
  • 每个请求复用同一个实例:通过中间件在请求开始时创建实例,避免重复初始化的性能损耗。
  • 保证TypeScript类型安全:显式声明BetterAuth类型,规避类型报错。

内容的提问来源于stack exchange,提问作者Nazim M.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 18:13:09