[GH-ISSUE #1375] Support Cloudflare Workers without nodejs_compat #17350

Closed
opened 2026-04-15 15:28:20 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @abegehr on GitHub (Feb 7, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1375

Is this suited for github?

  • Yes, this is suited for github

When running better-auth via Hono.js on a Cloudflare Worker, NodeJS apis are not available by default, leading to runtime error ReferenceError: Buffer is not defined, ref: https://github.com/better-auth/better-auth/issues/593#issuecomment-2488279122 (first reported by @lassegit).

As @avanderbergh points out here, Cloudflare can polyfill NodeJS APIs (including Buffer) using compatibility_flags = ["nodejs_compat"] which makes it run. 🎉

Describe the solution you'd like

It would be nice, to have better-auth support Coudflare Workers and other non-NodeJS runtimes by default. Since HonoJS uses non-NodeJS runtimes and is primarily placed in better-auth docs under backend.

Describe alternatives you've considered

Just enable compatibility_flags = ["nodejs_compat"].
Add to better-auth's Hono docs: https://www.better-auth.com/docs/integrations/hono

Additional context

No response

Originally created by @abegehr on GitHub (Feb 7, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1375 ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. When running better-auth via Hono.js on a Cloudflare Worker, NodeJS apis are not available by default, leading to runtime error `ReferenceError: Buffer is not defined`, ref: https://github.com/better-auth/better-auth/issues/593#issuecomment-2488279122 (first reported by @lassegit). As @avanderbergh points out [here](https://github.com/better-auth/better-auth/issues/593#issuecomment-2488412971), Cloudflare can polyfill NodeJS APIs (including Buffer) using `compatibility_flags = ["nodejs_compat"]` which makes it run. 🎉 ### Describe the solution you'd like It would be nice, to have better-auth support Coudflare Workers and other non-NodeJS runtimes by default. Since HonoJS uses non-NodeJS runtimes and is primarily placed in better-auth docs under backend. ### Describe alternatives you've considered Just enable `compatibility_flags = ["nodejs_compat"]`. Add to better-auth's Hono docs: https://www.better-auth.com/docs/integrations/hono ### Additional context _No response_
GiteaMirror added the locked label 2026-04-15 15:28:20 -05:00
Author
Owner

@Fawwaz-2009 commented on GitHub (Mar 17, 2025):

hey @abegehr I made an example for a hono server and separate frontend(s), hope it helps https://github.com/Fawwaz-2009/cloudflare-api-and-separate-frontend

<!-- gh-comment-id:2728560869 --> @Fawwaz-2009 commented on GitHub (Mar 17, 2025): hey @abegehr I made an example for a hono server and separate frontend(s), hope it helps https://github.com/Fawwaz-2009/cloudflare-api-and-separate-frontend
Author
Owner

@praneybehl commented on GitHub (Dec 11, 2025):

Workaround for createRequire(import.meta.url) Error on Cloudflare Workers

I encountered this issue when using BetterAuth 1.4.6 with Hono on Cloudflare Workers. The error occurs because better-auth's bundled code uses createRequire(import.meta.url) which fails in Workers since import.meta.url is undefined in the bundled output.

The Fix

1. Create a polyfill for node:module at src/polyfills/node-module.ts:

/**
 * Polyfill for node:module in Cloudflare Workers
 */
export function createRequire(_filename: string | URL): NodeRequire {
  const req = function require(id: string): never {
    throw new Error(
      \`Cannot use require() in Cloudflare Workers. \` +
        \`The module "${id}" must be imported using ESM import syntax.\`
    );
  };

  req.resolve = function resolve(id: string): never {
    throw new Error(
      \`Cannot use require.resolve() in Cloudflare Workers. \` +
        \`The module "${id}" must be resolved using ESM import syntax.\`
    );
  };

  req.resolve.paths = function paths(_request: string): null {
    return null;
  };

  req.cache = {};
  req.extensions = {};
  req.main = undefined;

  return req as unknown as NodeRequire;
}

export function isBuiltin(_moduleName: string): boolean {
  return false;
}

export function findSourceMap(_path: string): undefined {
  return undefined;
}

export function syncBuiltinESMExports(): void {}

export default { createRequire, isBuiltin, findSourceMap, syncBuiltinESMExports };

2. Add module aliasing in wrangler.toml:

name = "my-api"
main = "src/index.ts"
compatibility_date = "2025-09-01"
compatibility_flags = ["nodejs_compat_v2"]

[alias]
"node:module" = "./src/polyfills/node-module.ts"

Environment

  • Wrangler: 4.53.0
  • BetterAuth: 1.4.6
  • Hono: 4.7.0
  • Bun: 1.3.4

This workaround allows BetterAuth to run on Cloudflare Workers by providing a no-op createRequire that satisfies the module resolution without actually needing CommonJS support.

Hope this helps others facing the same issue!

<!-- gh-comment-id:3640522107 --> @praneybehl commented on GitHub (Dec 11, 2025): ## Workaround for `createRequire(import.meta.url)` Error on Cloudflare Workers I encountered this issue when using BetterAuth 1.4.6 with Hono on Cloudflare Workers. The error occurs because better-auth's bundled code uses `createRequire(import.meta.url)` which fails in Workers since `import.meta.url` is undefined in the bundled output. ### The Fix **1. Create a polyfill for `node:module`** at `src/polyfills/node-module.ts`: ```typescript /** * Polyfill for node:module in Cloudflare Workers */ export function createRequire(_filename: string | URL): NodeRequire { const req = function require(id: string): never { throw new Error( \`Cannot use require() in Cloudflare Workers. \` + \`The module "${id}" must be imported using ESM import syntax.\` ); }; req.resolve = function resolve(id: string): never { throw new Error( \`Cannot use require.resolve() in Cloudflare Workers. \` + \`The module "${id}" must be resolved using ESM import syntax.\` ); }; req.resolve.paths = function paths(_request: string): null { return null; }; req.cache = {}; req.extensions = {}; req.main = undefined; return req as unknown as NodeRequire; } export function isBuiltin(_moduleName: string): boolean { return false; } export function findSourceMap(_path: string): undefined { return undefined; } export function syncBuiltinESMExports(): void {} export default { createRequire, isBuiltin, findSourceMap, syncBuiltinESMExports }; ``` **2. Add module aliasing in `wrangler.toml`**: ```toml name = "my-api" main = "src/index.ts" compatibility_date = "2025-09-01" compatibility_flags = ["nodejs_compat_v2"] [alias] "node:module" = "./src/polyfills/node-module.ts" ``` ### Environment - Wrangler: 4.53.0 - BetterAuth: 1.4.6 - Hono: 4.7.0 - Bun: 1.3.4 This workaround allows BetterAuth to run on Cloudflare Workers by providing a no-op `createRequire` that satisfies the module resolution without actually needing CommonJS support. Hope this helps others facing the same issue\!
Author
Owner

@praneybehl commented on GitHub (Dec 11, 2025):

Technical Analysis & Suggested Fix for Core Team

After debugging this issue in depth, here's a technical breakdown of the root cause and potential solutions for the BetterAuth team.

Root Cause Analysis

The issue stems from Rolldown's bundler runtime helpers in chunk-DieNfLhd.mjs:

import { createRequire } from "node:module";
var __require = /* @__PURE__ */ createRequire(import.meta.url);

This __require is then used throughout the bundle to dynamically require Node.js built-ins:

// esm-CyZgw_uF.mjs (appears to be postgres driver code)
const util = __require("util");
const nodeCrypto = __require("crypto");
const fs = config.sslcert ? __require("fs") : null;
const dns = __require("dns");
const { EventEmitter } = __require("events");
return new (__require("net")).Socket();
return __require("tls").connect(options);

// promise-CL99vzc3.mjs
var buffer = __require("buffer");
var StringDecoder = __require("string_decoder").StringDecoder;
streamModule = __require("stream");
const crypto = __require("crypto");
const Timers = __require("timers");

The problem: import.meta.url is undefined in Cloudflare Workers' bundled output, so createRequire(undefined) throws immediately at module initialization.

Suggested Solutions

Modify the build configuration to use a conditional createRequire that handles edge runtimes:

// Instead of:
var __require = createRequire(import.meta.url);

// Use:
var __require = (() => {
  if (typeof import.meta.url !== 'undefined') {
    try {
      return createRequire(import.meta.url);
    } catch {}
  }
  // Fallback for edge runtimes - return a function that uses dynamic import
  return (id) => {
    throw new Error(`Dynamic require of "${id}" is not supported in this runtime. Use ESM imports instead.`);
  };
})();

Option 2: Use ESM Imports with node: Prefix

Since Cloudflare Workers with nodejs_compat_v2 supports Node.js built-ins via ESM imports, consider restructuring to use static ESM imports:

// Instead of dynamic require
const crypto = __require("crypto");

// Use static ESM import at top of module
import crypto from "node:crypto";

This would require changes to how dependencies (particularly the Postgres drivers like pg or postgres) are bundled.

Option 3: Rolldown Configuration

If using Rolldown for bundling, configure it to:

  1. Mark Node.js built-ins as external
  2. Use a custom createRequire shim that's edge-runtime aware
  3. Or use output.generatedCode.symbols: true with proper polyfills

Option 4: Separate Edge-Compatible Bundle

Create a separate bundle entry point for edge runtimes that:

  1. Uses only ESM imports
  2. Excludes or replaces Node.js-specific code paths
  3. Uses edge-compatible alternatives (e.g., Web Crypto API instead of node:crypto)

Why This Matters

The current approach fails at module initialization time - before any user code runs. This means:

  • The error occurs even if the code path requiring fs, net, tls is never executed
  • Users can't catch or handle the error
  • Even nodejs_compat_v2 doesn't help because import.meta.url remains undefined

Files Affected

dist/chunk-DieNfLhd.mjs  - Runtime helper definition
dist/esm-CyZgw_uF.mjs    - ~20 __require calls (postgres driver)
dist/promise-CL99vzc3.mjs - ~15 __require calls (async utilities)

Testing Recommendation

To validate any fix, test with:

# wrangler.toml
compatibility_date = "2025-09-01"
compatibility_flags = ["nodejs_compat_v2"]
# NO [alias] section - should work natively

Happy to help test any proposed fixes!

<!-- gh-comment-id:3640530884 --> @praneybehl commented on GitHub (Dec 11, 2025): ## Technical Analysis & Suggested Fix for Core Team After debugging this issue in depth, here's a technical breakdown of the root cause and potential solutions for the BetterAuth team. ### Root Cause Analysis The issue stems from Rolldown's bundler runtime helpers in `chunk-DieNfLhd.mjs`: ```javascript import { createRequire } from "node:module"; var __require = /* @__PURE__ */ createRequire(import.meta.url); ``` This `__require` is then used throughout the bundle to dynamically require Node.js built-ins: ```javascript // esm-CyZgw_uF.mjs (appears to be postgres driver code) const util = __require("util"); const nodeCrypto = __require("crypto"); const fs = config.sslcert ? __require("fs") : null; const dns = __require("dns"); const { EventEmitter } = __require("events"); return new (__require("net")).Socket(); return __require("tls").connect(options); // promise-CL99vzc3.mjs var buffer = __require("buffer"); var StringDecoder = __require("string_decoder").StringDecoder; streamModule = __require("stream"); const crypto = __require("crypto"); const Timers = __require("timers"); ``` **The problem:** `import.meta.url` is `undefined` in Cloudflare Workers' bundled output, so `createRequire(undefined)` throws immediately at module initialization. ### Suggested Solutions #### Option 1: Conditional Runtime Helper (Recommended) Modify the build configuration to use a conditional `createRequire` that handles edge runtimes: ```javascript // Instead of: var __require = createRequire(import.meta.url); // Use: var __require = (() => { if (typeof import.meta.url !== 'undefined') { try { return createRequire(import.meta.url); } catch {} } // Fallback for edge runtimes - return a function that uses dynamic import return (id) => { throw new Error(`Dynamic require of "${id}" is not supported in this runtime. Use ESM imports instead.`); }; })(); ``` #### Option 2: Use ESM Imports with `node:` Prefix Since Cloudflare Workers with `nodejs_compat_v2` supports Node.js built-ins via ESM imports, consider restructuring to use static ESM imports: ```javascript // Instead of dynamic require const crypto = __require("crypto"); // Use static ESM import at top of module import crypto from "node:crypto"; ``` This would require changes to how dependencies (particularly the Postgres drivers like `pg` or `postgres`) are bundled. #### Option 3: Rolldown Configuration If using Rolldown for bundling, configure it to: 1. Mark Node.js built-ins as external 2. Use a custom `createRequire` shim that's edge-runtime aware 3. Or use `output.generatedCode.symbols: true` with proper polyfills #### Option 4: Separate Edge-Compatible Bundle Create a separate bundle entry point for edge runtimes that: 1. Uses only ESM imports 2. Excludes or replaces Node.js-specific code paths 3. Uses edge-compatible alternatives (e.g., Web Crypto API instead of `node:crypto`) ### Why This Matters The current approach fails at **module initialization time** - before any user code runs. This means: - The error occurs even if the code path requiring `fs`, `net`, `tls` is never executed - Users can't catch or handle the error - Even `nodejs_compat_v2` doesn't help because `import.meta.url` remains undefined ### Files Affected ``` dist/chunk-DieNfLhd.mjs - Runtime helper definition dist/esm-CyZgw_uF.mjs - ~20 __require calls (postgres driver) dist/promise-CL99vzc3.mjs - ~15 __require calls (async utilities) ``` ### Testing Recommendation To validate any fix, test with: ```toml # wrangler.toml compatibility_date = "2025-09-01" compatibility_flags = ["nodejs_compat_v2"] # NO [alias] section - should work natively ``` Happy to help test any proposed fixes!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#17350