[GH-ISSUE #207] Can't bind D1 Database on Hono with Cloudflare Workers #25495

Closed
opened 2026-04-17 15:44:54 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @serban-mihai on GitHub (Oct 17, 2024).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/207

After some attempts, I couldn't find a way to bind a D1 Database to better-auth on a Hono app deployed to Cloudflare Workers.

Following the official instructions, the installation step is common to all frameworks. The problem is that in the Cloudflare Workers environment, no context is available outside any inbound request, aka can't reference a Binding (such as a D1 Database or a Env Variable) from outside the request itself.

import { betterAuth } from "better-auth";
import { anonymous } from "better-auth/plugins";
import { Kysely } from "kysely";
import { D1Dialect } from "kysely-d1";

export const auth = betterAuth({
  database: {
    db: new Kysely({
      dialect: new D1Dialect({
        database: process.env.DB as unknown as D1Database, // this results in database: undefined
      }),
    }),
    type: "sqlite",
  },
  emailAndPassword: {
    enabled: true,
  }
});

Thanks to @Bekacru we got an example making it work in an OpenNext app that is still deployed to Workers but has a dedicated Bindings method to request them at need.
Hono also seems to have a method of such but I haven't been able to make it work:
/src/index.ts

...
app.use(contextStorage())
...

/src/lib/auth.ts

...
import { getContext } from "hono/context-storage";
import type { Session, User } from "better-auth";

type Context = {
  Bindings: {
    DB: D1Database
  },
  Variables: {
    user: User | null,
    session: Session | null
  }
}

export const auth = betterAuth({
  database: {
    db: new Kysely({
      dialect: new D1Dialect({
        database: getContext<Context>().env.DB as D1Database,
      }),
    }),
    type: "sqlite",
  },
  emailAndPassword: {
    enabled: true,
  }
});

When running npx @better-auth/cli generate with the dev server up:

ERROR [#better-auth]: Couldn't read your auth config. Context is not available

Typescript linting works though...

Some problems:

  • It's impossible to use process.env.DB to get the DB binding from D1 because the file where the auth is created is outside the request context scope (in Hono is c.env.DB)
  • Can't use a middleware in Hono to create the config file with each request for the same reason above
  • The library requires an auth object exported from a specific path within the project tree, otherwise, the CLI won't be able to make generations nor migrations (I know there is the --config flag but it's still a problem not having the auth object exported)

If someone can make it work, I would like to request an example of Hono that uses the D1 Database, or any other database.

I've been successful using SolidStart with better-auth deployed to Cloudflare Pages, which is connected through Service Binding to the same Hono Worker, but I need the server-side of better-auth to be accessible on Hono directly, for sanity reasons and since I have the API there and I need to check against sessions at each request.

More about this topic:

Originally created by @serban-mihai on GitHub (Oct 17, 2024). Original GitHub issue: https://github.com/better-auth/better-auth/issues/207 After some attempts, I couldn't find a way to bind a D1 Database to `better-auth` on a Hono app deployed to Cloudflare Workers. Following the [official instructions](https://www.better-auth.com/docs/integrations/hono), the installation step is common to all frameworks. The problem is that in the Cloudflare Workers environment, no context is available outside any inbound request, aka can't reference a Binding (such as a D1 Database or a Env Variable) from outside the request itself. ```typescript import { betterAuth } from "better-auth"; import { anonymous } from "better-auth/plugins"; import { Kysely } from "kysely"; import { D1Dialect } from "kysely-d1"; export const auth = betterAuth({ database: { db: new Kysely({ dialect: new D1Dialect({ database: process.env.DB as unknown as D1Database, // this results in database: undefined }), }), type: "sqlite", }, emailAndPassword: { enabled: true, } }); ``` Thanks to @Bekacru we got an [example](https://github.com/Bekacru/better-auth-nextjs-cf-d1-example) making it work in an OpenNext app that is still deployed to Workers but has a [dedicated Bindings method](https://opennext.js.org/cloudflare/bindings) to request them at need. Hono also seems to have a [method of such](https://hono.dev/docs/middleware/builtin/context-storage#usage) but I haven't been able to make it work: `/src/index.ts` ```typescript ... app.use(contextStorage()) ... ``` `/src/lib/auth.ts` ```typescript ... import { getContext } from "hono/context-storage"; import type { Session, User } from "better-auth"; type Context = { Bindings: { DB: D1Database }, Variables: { user: User | null, session: Session | null } } export const auth = betterAuth({ database: { db: new Kysely({ dialect: new D1Dialect({ database: getContext<Context>().env.DB as D1Database, }), }), type: "sqlite", }, emailAndPassword: { enabled: true, } }); ``` When running `npx @better-auth/cli generate` with the dev server up: > ERROR [#better-auth]: Couldn't read your auth config. Context is not available Typescript linting works though... Some problems: - It's impossible to use `process.env.DB` to get the DB binding from D1 because the file where the auth is created is outside the request context scope (in Hono is `c.env.DB`) - Can't use a middleware in Hono to create the config file with each request for the same reason above - The library requires an `auth` object exported from a specific path within the project tree, otherwise, the CLI won't be able to make `generations` nor `migrations` (I know there is the **--config** flag but it's still a problem not having the auth object exported) If someone can make it work, I would like to request an example of Hono that uses the D1 Database, or any other database. > I've been successful using SolidStart with better-auth deployed to Cloudflare Pages, which is connected through Service Binding to the same Hono Worker, but I need the server-side of better-auth to be accessible on Hono directly, for sanity reasons and since I have the API there and I need to check against sessions at each request. More about this topic: - [Discord: Hono](https://discord.com/channels/1011308539819597844/1296154637581160548) - [Discord: Better Auth](https://discord.com/channels/1288403910284935179/1296184019029065778) - [GitHub Issue](https://github.com/better-auth/better-auth/issues/175)
GiteaMirror added the locked label 2026-04-17 15:44:55 -05:00
Author
Owner

@serban-mihai commented on GitHub (Oct 18, 2024):

Still grinding it.

Some updates:

  • The first line of the docs states that This integration guide is assuming you are using Hono with node server, meaning that edge or serverless environments might not be supported. Daily reminder to read the docs in full, my fault.
  • Although I've been able to make it work with a middleware with D1, the only thing I'm missing is the CLI to be able to pick up the auth config and generate and migrate schemas.

I'll look into the CLI code to see if there's a viable solution for it to get the auth config in a more declarative way, so that even when built for edge environments, will still be able to make migrations.

.dev.vars

BETTER_AUTH_SECRET="<your-secret-here>"
BETTER_AUTH_URL="<your-server-url-here>"
BETTER_AUTH_TRUSTED_ORIGINS="<server-origin-1>,<server-origin-2>"

auth.ts

import type { Session, User, Auth } from "better-auth";
import { betterAuth } from "better-auth";
import { anonymous } from "better-auth/plugins";
import { Kysely } from "kysely";
import { D1Dialect } from "kysely-d1";
import { Context } from "hono";

export type Environment = {
  Bindings: {
     DB: D1Database;
     BETTER_AUTH_SECRET: string;
     BETTER_AUTH_URL: string;
     BETTER_AUTH_TRUSTED_ORIGINS: string;
  };
  Variables: {
    user: User | null;
    session: Session | null;
  };
};

export const initAuth = (c: Context<Environment>): Auth => {
  return betterAuth({
    database: {
      db: new Kysely({
        dialect: new D1Dialect({
          database: c.env.DB as D1Database,
        }),
      }),
      type: "sqlite",
    },
    secret: c.env.BETTER_AUTH_SECRET,
    baseURL: c.env.BETTER_AUTH_URL,
    trustedOrigins: c.env.BETTER_AUTH_TRUSTED_ORIGINS.split(","),
    emailAndPassword: {
      enabled: true,
    }
  });
};

AuthMiddleware.ts

import type { Auth } from "better-auth";
import { initAuth } from "@src/lib/auth";
import { createMiddleware } from "hono/factory";

export const authMiddleware = createMiddleware(async (c, next) => {
  const auth: Auth = initAuth(c);
  const session = await auth.api.getSession({ headers: c.req.raw.headers });

  if (!session) {
    c.set("user", null);
    c.set("session", null);
    return await next();
  }

  c.set("user", session.user);
  c.set("session", session.session);
  return await next();
});

index.ts

app.use("*", async (c, next) => await authMiddleware(c, next));

app.on(["POST", "GET"], "/api/auth/**", (c) => {
  const auth: Auth = initAuth(c);
  return auth.handler(c.req.raw);
});
<!-- gh-comment-id:2422179323 --> @serban-mihai commented on GitHub (Oct 18, 2024): Still grinding it. Some updates: - The first line of the docs states that [This integration guide is assuming you are using Hono with node server](https://www.better-auth.com/docs/integrations/hono), meaning that `edge` or `serverless` environments might not be supported. Daily reminder to read the docs in full, my fault. - Although I've been able to make it work with a middleware with D1, the only thing I'm missing is the CLI to be able to pick up the auth config and `generate` and `migrate` schemas. I'll look into the CLI code to see if there's a viable solution for it to get the auth config in a more declarative way, so that even when built for edge environments, will still be able to make migrations. `.dev.vars` ```typescript BETTER_AUTH_SECRET="<your-secret-here>" BETTER_AUTH_URL="<your-server-url-here>" BETTER_AUTH_TRUSTED_ORIGINS="<server-origin-1>,<server-origin-2>" ``` `auth.ts` ```typescript import type { Session, User, Auth } from "better-auth"; import { betterAuth } from "better-auth"; import { anonymous } from "better-auth/plugins"; import { Kysely } from "kysely"; import { D1Dialect } from "kysely-d1"; import { Context } from "hono"; export type Environment = { Bindings: { DB: D1Database; BETTER_AUTH_SECRET: string; BETTER_AUTH_URL: string; BETTER_AUTH_TRUSTED_ORIGINS: string; }; Variables: { user: User | null; session: Session | null; }; }; export const initAuth = (c: Context<Environment>): Auth => { return betterAuth({ database: { db: new Kysely({ dialect: new D1Dialect({ database: c.env.DB as D1Database, }), }), type: "sqlite", }, secret: c.env.BETTER_AUTH_SECRET, baseURL: c.env.BETTER_AUTH_URL, trustedOrigins: c.env.BETTER_AUTH_TRUSTED_ORIGINS.split(","), emailAndPassword: { enabled: true, } }); }; ``` `AuthMiddleware.ts` ```typescript import type { Auth } from "better-auth"; import { initAuth } from "@src/lib/auth"; import { createMiddleware } from "hono/factory"; export const authMiddleware = createMiddleware(async (c, next) => { const auth: Auth = initAuth(c); const session = await auth.api.getSession({ headers: c.req.raw.headers }); if (!session) { c.set("user", null); c.set("session", null); return await next(); } c.set("user", session.user); c.set("session", session.session); return await next(); }); ``` `index.ts` ```typescript app.use("*", async (c, next) => await authMiddleware(c, next)); app.on(["POST", "GET"], "/api/auth/**", (c) => { const auth: Auth = initAuth(c); return auth.handler(c.req.raw); }); ```
Author
Owner

@Bekacru commented on GitHub (Nov 17, 2024):

you can use what this project in Nuxt did to migrate tables with Cloudflare by setting up an endpoint that triggers the migration

https://github.com/atinux/nuxthub-better-auth

<!-- gh-comment-id:2480983376 --> @Bekacru commented on GitHub (Nov 17, 2024): you can use what this project in Nuxt did to migrate tables with Cloudflare by setting up an endpoint that triggers the migration https://github.com/atinux/nuxthub-better-auth
Author
Owner

@VesperQuartz commented on GitHub (Jan 25, 2026):

How is This solved? Is the cli working with this config?

<!-- gh-comment-id:3797251472 --> @VesperQuartz commented on GitHub (Jan 25, 2026): How is This solved? Is the cli working with this config?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#25495