1---
2title: Cloudflare Hyperdrive
3description: Deploy a Better Auth Worker with an uncached Hyperdrive connection to a Neon Postgres origin.
4---
5
6This example provisions a Neon Postgres database, a Hyperdrive connection, and a Better Auth Worker. Hyperdrive provides pooled access to the origin; it does not create the origin database itself.
7
8## Install
9
10```sh
11bun add @alchemy.run/better-auth better-auth@^1.7.5 kysely pg
12```
13
14Configure Cloudflare and Neon through [Alchemy profiles](/environments/profiles), or supply `NEON_API_KEY` for Neon. If you only need direct Neon access, the [Neon serverless layer](/better-auth/databases/neon) avoids Hyperdrive and `pg`.
15
16## Define the database layer
17
18```typescript title="src/auth-database.ts"
19import { CloudflareHyperdrive } from "@alchemy.run/better-auth/CloudflareHyperdrive";
20import * as Cloudflare from "alchemy/Cloudflare";
21import * as Neon from "alchemy/Neon";
22import * as Effect from "effect/Effect";
23import * as Layer from "effect/Layer";
24
25const AuthDb = Neon.Project("AuthDb");
26
27export const AuthDatabase = Layer.unwrap(
28 Effect.gen(function* () {
29 const db = yield* AuthDb;
30 const connection = yield* Cloudflare.Hyperdrive.Connection("AuthConnection", {
31 origin: db.origin,
32 caching: { disabled: true },
33 });
34 return CloudflareHyperdrive(connection, {
35 migrate: db.connectionUri,
36 });
37 }),
38);
39```
40
41Keep Hyperdrive caching disabled so session and verification reads cannot return stale data. Migrations connect directly to the origin URL because Hyperdrive's runtime connection string exists only inside the Worker.
42
43## Define the auth Worker
44
45```typescript title="src/worker.ts"
46import { BetterAuth } from "@alchemy.run/better-auth";
47import * as Cloudflare from "alchemy/Cloudflare";
48import * as Effect from "effect/Effect";
49import { AuthDatabase } from "./auth-database.ts";
50
51export default class AuthApi extends Cloudflare.Worker<AuthApi>()(
52 "AuthApi",
53 {
54 main: import.meta.url,
55 compatibility: { flags: ["nodejs_compat"] },
56 },
57 Effect.gen(function* () {
58 const auth = yield* BetterAuth({
59 basePath: "/api/auth",
60 emailAndPassword: { enabled: true },
61 });
62 return { fetch: auth.fetch };
63 }).pipe(Effect.provide(AuthDatabase)),
64) {}
65```
66
67The layer registers Hyperdrive's native binding and opens a driver pool inside each request, never during Worker construction. Better Auth's signing secret is generated and bound automatically.
68
69## Define the stack
70
71```typescript title="alchemy.run.ts"
72import * as Alchemy from "alchemy";
73import * as Cloudflare from "alchemy/Cloudflare";
74import * as Neon from "alchemy/Neon";
75import * as Effect from "effect/Effect";
76import * as Layer from "effect/Layer";
77import AuthApi from "./src/worker.ts";
78
79export default Alchemy.Stack(
80 "BetterAuthHyperdrive",
81 {
82 providers: Layer.mergeAll(Cloudflare.providers(), Neon.providers()),
83 state: Alchemy.localState(),
84 },
85 Effect.gen(function* () {
86 const api = yield* AuthApi;
87 return { url: api.url.as<string>() };
88 }),
89);
90```
91
92Preserve and protect the local `.alchemy` state directory so later deployments retain resource identities and the signing secret.
93
94## Deploy and migrate through the origin
95
96```sh
97bunx alchemy deploy
98```
99
100The deployer must reach the origin with schema-changing permissions; the migration completes before the Worker is updated. Set `AUTH_ORIGIN` to the printed URL and verify the endpoint:
101
102```sh
103curl -i "$AUTH_ORIGIN/api/auth/get-session"
104```
105
106Omitting the database layer's `migrate` source disables automatic migration support. In that case, leave Better Auth's `migrate` unset or false and manage the schema yourself; explicitly requesting `migrate: true` fails.
107
108## MySQL origins
109
110For an existing MySQL origin, install its driver instead of `pg`:
111
112```sh
113bun add mysql2
114```
115
116Configure the Hyperdrive resource with that MySQL origin, select `dialect: "mysql"` in `CloudflareHyperdrive`, and supply the origin's deploy-resolvable URL as `migrate`. Keep caching disabled; changing the dialect alone neither converts the Neon origin above nor moves existing auth records.
117
118The `pool` option passes through to the selected driver, and migration credentials may come from a resource Output without binding them to the Worker. See [configuration](/better-auth/guides/configuration), [migrations](/better-auth/guides/migrations), the [upgrade guide](/better-auth/upgrades/from-1-6-to-1-7), and the [`CloudflareHyperdrive` reference](/providers/betterauth/cloudflarehyperdrive).