cloudflare / cloudflare/vinext
Alternating request failures (success, fail, success...) when using Drizzle + Postgres lazily evaluated in vinext / Cloudflare Workers
- Dominant language
- TypeScript
- Stars
- 8.8k
- Forks
- 406
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 120
Description
## Description
I am deploying a Next.js application to Cloudflare Workers (using vinext). I am using Drizzle ORM with the postgres driver connecting to a Supabase database.
Because Cloudflare Workers do not allow asynchronous I/O (like establishing a database connection) in the global scope, the standard Drizzle initialization fails during the build step. To bypass this, I implemented a lazy initialization pattern using a JavaScript Proxy.
While this successfully builds on both Vercel and Cloudflare Workers, runtime behavior on Cloudflare is inconsistent. Specifically, requests fail exactly every other time:
* 1st Request: Success
* 2nd Request: Fail
* 3rd Request: Success
* 4th Request: Fail
(This happens regardless of whether I use Hyperdrive or a DB pooler URL).
---
## Steps to Reproduce
### Attempt 1: Standard Drizzle Setup (Fails during build)
If I use the standard initialization, the worker fails to build/deploy due to global scope I/O restrictions.
```ts
import * as schema from "./schemas/schema-main";
import * as schema2 from "./schemas/schema-secondary";
import { drizzle } from 'drizzle-orm/postgres-js'
import { env } from "cloudflare:workers";
import postgres from 'postgres'
const poolDb = postgres(
env.HYPERDRIVE.connectionString ?? "", {
prepare: false,
});
export const db = drizzle({
client: poolDb,
schema: schema,
})
```
### Build Error (Attempt 1)
```
16:48:27.291 ✘ [ERROR] A request to the Cloudflare API (/accounts//workers/scripts//versions) failed.
16:48:27.291 Uncaught Error: Disallowed operation called within global scope. Asynchronous I/O (ex: fetch() or connect()), setting a timeout, and generating random values are not allowed within global scope. To fix this error, perform this operation within a handler. https://developers.cloudflare.com/workers/runtime-apis/handlers/
16:48:27.291 at null. (index.js:36463:41)
16:48:27.291 [code: 10021]
```
---
### Attempt 2: Lazy Evaluation using Proxy (Builds successfully, but runtime alternates failing)
To fix the build error, I used a Proxy to lazily instantiate the database connection only when a query is actually executed.
```ts
import * as schema from "./schemas/schema-main";
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
function isCloudflareWorkersRuntime(): boolean {
return (
typeof navigator !== "undefined" &&
navigator.userAgent === "Cloudflare-Workers"
);
}
// PRIMARY DB LAZY INIT
let _db: ReturnType | null = null;
export const db = new Proxy({} as any, {
get: (target, prop) => {
if (!_db) {
function connectionString() {
if (isCloudflareWorkersRuntime()) {
const { env } = require("cloudflare:workers");
return env.HYPERDRIVE.connectionString ?? "";
}
return process.env.MY_PRIMARY_DB_URL ?? "";
};
const poolDb = postgres(
connectionString(), {
prepare: false,
});
_db = drizzle({
client: poolDb,
schema: schema,
});
}
return (_db as any)[prop];
}
}) as ReturnType;
```
---
## Expected Behavior
The lazy evaluated database connection should remain persistent and successfully resolve requests consistently on Cloudflare Workers, just as it does on Vercel.
---
## Environment
* **Framework:** Vinext (0.0.30) and NextJS (16.1.6)
* **Database:** Supabase (Postgres)
* **ORM:** Drizzle ORM (`drizzle-orm/postgres-js`)
* **Driver:** `postgres`
* **Cloudflare Bindings:** Hyperdrive
(Also tested without Hyperdrive using standard Pooler URLs via `process.env`, but the alternating failure behavior remains exactly the same).
Contributor guide
Assessment
This issue has not been assessed yet.