Neon
This example provisions Neon and serves Better Auth from a Cloudflare Worker. The serverless driver uses WebSockets, so it needs neither Hyperdrive nor pg.
Install
Section titled “Install”bun add @alchemy.run/better-auth better-auth@^1.7.5 kysely @neondatabase/serverlessConfigure Cloudflare and Neon through Alchemy profiles, or supply NEON_API_KEY for Neon. You provide account credentials; the project resource creates the database, not the API key.
Define the database layer
Section titled “Define the database layer”import { Neon as NeonDatabase } from "@alchemy.run/better-auth/Neon";import * as Neon from "alchemy/Neon";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";
const AuthDb = Neon.Project("AuthDb");
export const AuthDatabase = Layer.unwrap( Effect.map(AuthDb, (db) => NeonDatabase(db.connectionUri)),);The project’s connectionUri Output is bound into the host environment and also supplies deploy-time migrations. The driver acquires one pool per request and closes it when that request settles.
Define the auth Worker
Section titled “Define the auth Worker”import { BetterAuth } from "@alchemy.run/better-auth";import * as Cloudflare from "alchemy/Cloudflare";import * as Effect from "effect/Effect";import { AuthDatabase } from "./auth-database.ts";
export default class AuthApi extends Cloudflare.Worker<AuthApi>()( "AuthApi", { main: import.meta.url, compatibility: { flags: ["nodejs_compat"] }, }, Effect.gen(function* () { const auth = yield* BetterAuth({ basePath: "/api/auth", emailAndPassword: { enabled: true }, }); return { fetch: auth.fetch }; }).pipe(Effect.provide(AuthDatabase)),) {}This is an auth-only endpoint, with no frontend or application routes. Alchemy generates and binds the signing secret automatically.
Define the stack
Section titled “Define the stack”import * as Alchemy from "alchemy";import * as Cloudflare from "alchemy/Cloudflare";import * as Neon from "alchemy/Neon";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import AuthApi from "./src/worker.ts";
export default Alchemy.Stack( "BetterAuthNeon", { providers: Layer.mergeAll(Cloudflare.providers(), Neon.providers()), state: Alchemy.localState(), }, Effect.gen(function* () { const api = yield* AuthApi; return { url: api.url.as<string>() }; }),);Keep the local .alchemy state directory protected and persistent so subsequent deployments retain resource identities and the signing secret.
Deploy and verify
Section titled “Deploy and verify”bunx alchemy deployThe deployer must reach Neon with schema-changing permissions; migrations complete before the Worker is updated. Set AUTH_ORIGIN to the printed URL to check the endpoint:
curl -i "$AUTH_ORIGIN/api/auth/get-session"Connect an existing project
Section titled “Connect an existing project”To use an existing Neon URL, replace only src/auth-database.ts with this complete file:
import { Neon } from "@alchemy.run/better-auth/Neon";import * as Config from "effect/Config";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";
export const AuthDatabase = Layer.unwrap( Effect.gen(function* () { const url = yield* Config.Redacted("DATABASE_URL"); return Neon(url); }),);Set DATABASE_URL in the deploying environment; it is read and bound during host construction, not first discovered inside a request. The stack above still works, but this variant creates no Neon resource and does not need Neon API credentials.
Other connection layouts
Section titled “Other connection layouts”The same database layer supports Lambda, where the pool is scoped to each invocation. For stage isolation, a Neon.Branch connection URI can replace the project URI; register Neon.providers() whenever the stack provisions Neon resources.
The layer’s migrate option accepts a separate deploy-resolvable connection source, or false to disable its migration support. Configuration read during host construction is bound to the host, so use a resource Output for migration credentials that must remain deploy-only.
Automatic migrations do not move existing auth records between databases. See configuration, migrations, the upgrade guide, and the Neon reference.