Skip to content

Alternating request failures (success, fail, success...) when using Drizzle + Postgres lazily evaluated in vinext / Cloudflare Workers #537

@sindhukhrisna

Description

@sindhukhrisna

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.

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/<ACCOUNT_ID>/workers/scripts/<PROJECT_NAME>/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.<anonymous> (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.

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<typeof drizzle> | 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<typeof drizzle>;

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions