Skip to content

Conversation

agcty
Copy link

@agcty agcty commented Oct 18, 2025

Improves local-first development workflow by making the origin property optional when only dev.origin is provided. This matches the
wrangler.jsonc pattern where developers can use a local connection string without needing production database credentials.

Changes

  • Made origin optional in HyperdriveProps interface
  • Added runtime validation that throws a clear error if deploying to production without origin
  • In local mode, origin falls back to dev.origin if not provided
  • Updated documentation with "Local Development Only" example showing the new pattern
  • All existing tests pass (fully backwards compatible)

Example Usage

Before (required production credentials even for local dev):

const db = await Hyperdrive("my-db", {
  origin: "postgres://placeholder:placeholder@localhost:5432/placeholder", // ❌ needed fake credentials
  dev: {
    origin: "postgres://postgres:postgres@localhost:5432/postgres"
  }
});

After (local-first development):
const db = await Hyperdrive("my-db", {
  dev: {
    origin: "postgres://postgres:postgres@localhost:5432/postgres"
  }
  // ✅ origin omitted until production credentials are available
});

Test Plan

- All 10 Hyperdrive tests pass
- Create/update/delete operations work correctly
- Adoption flow works correctly
- Normalization tests for all origin formats pass
- Backwards compatible - existing code with origin continues to work

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
* Added support for local-only development with Cloudflare Hyperdrive, allowing optional origin configuration in development environments.
* Added a new example project demonstrating Cloudflare Hyperdrive setup for local development workflows.

* **Documentation**
* Updated Cloudflare Hyperdrive documentation with local development guidance and production deployment requirements.

* **Bug Fixes**
* Production deployments now properly validate that origin is provided for Hyperdrive configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

JacobMGEvans and others added 7 commits October 9, 2025 20:13
…1093)

* test(cloudflare): comprehensive dead letter queue tests

Adds comprehensive test coverage for the dead letter queue feature in
QueueConsumer, which was fixed in PR alchemy-run#1092.

Tests cover:
- DLQ with string reference (queue name)
- DLQ with Queue object reference
- Updating consumer to add DLQ
- Worker eventSources with DLQ settings

Also includes the missing fix for build-worker-options.ts to properly
handle deadLetterQueue in Miniflare local development, and updates
queue-consumer.ts to preserve DLQ settings from props when the API
doesn't return them in the response.

Related: alchemy-run#1092

* test(cloudflare): comprehensive dead letter queue tests

Adds comprehensive test coverage for the dead letter queue feature in
QueueConsumer, which was fixed in PR alchemy-run#1092.

Tests cover:
- DLQ with string reference (queue name)
- DLQ with Queue object reference
- Updating consumer to add DLQ
- Worker eventSources with DLQ settings

Also includes the missing fix for build-worker-options.ts to properly
handle deadLetterQueue in Miniflare local development, and updates
queue-consumer.ts to preserve DLQ settings from props when the API
doesn't return them in the response.

Related: alchemy-run#1092

* Improved the DLQ test and fixed the typings for being root not in settings

* Removed DLQ response, since Cloudflare missed that DX in the API we will callback to the props instead for something to be there for the user

---------

Co-authored-by: Sam Goodwin <[email protected]>
Improves local-first development workflow by making the `origin` property optional when only `dev.origin` is provided. This matches the wrangler.jsonc pattern where developers can use a local connection string without needing production database credentials.

Changes:
- Made `origin` optional in `HyperdriveProps`
- Added runtime validation that throws a clear error if deploying to production without `origin`
- In local mode, `origin` falls back to `dev.origin` if not provided
- Updated documentation with "Local Development Only" example
- All existing tests pass (fully backwards compatible)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes make the origin property optional in HyperdriveProps, add conditional logic to enforce production origin requirements while supporting local development with dev.origin fallback, update documentation with local development guidance, introduce a new test for the production validation error, and create a new example project demonstrating dev-only Hyperdrive setup.

Changes

Cohort / File(s) Summary
Core Hyperdrive Implementation
alchemy/src/cloudflare/hyperdrive.ts, alchemy/test/cloudflare/hyperdrive.test.ts
Made origin optional in HyperdriveProps; added production/local mode branching to enforce origin requirement in production and fall back to dev.origin in local mode; added test case validating error when production origin is missing
Documentation
alchemy-web/src/content/docs/providers/cloudflare/hyperdrive.md
Added "Local Development Only" section with code examples and caution note explaining origin requirement for production deployment
New Example Project
examples/cloudflare-dev-only-hyperdrive/*
Created complete example project including package.json (with build/deploy/destroy scripts and dependencies), alchemy.run.ts (Hyperdrive configuration), src/worker.ts (Cloudflare Worker with PostgreSQL integration), README.md (setup and usage instructions), and tsconfig.json
Formatting & Housekeeping
examples/cloudflare-tanstack-start/src/routeTree.gen.ts, package.json, tests/smoke-test-flatten-website/package.json
Applied quote style normalization and formatting adjustments in routeTree.gen.ts; added trailing newline to package.json; updated alchemy devDependency version from workspace:* to 0.62.2 in smoke tests

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Hyperdrive
    participant Config
    participant Error

    Caller->>Hyperdrive: Create with optional origin
    
    alt Production Mode (not local scope)
        Hyperdrive->>Config: Check if origin provided
        alt Origin Missing
            Config->>Error: Throw "requires 'origin' for production deployment"
            Error-->>Caller: Error
        else Origin Provided
            Config->>Hyperdrive: Use production origin
            Hyperdrive-->>Caller: Success
        end
    else Local Mode
        Hyperdrive->>Config: origin not provided
        Config->>Hyperdrive: Use dev.origin fallback
        Hyperdrive-->>Caller: Success with dev connection
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

The changes involve conditional logic modifications in the core Hyperdrive implementation with production/local branching, new test coverage, documentation updates, and a complete new example project. The logic is moderately dense but localized to origin handling, making review straightforward for each cohort despite the file spread.

Suggested reviewers

  • JacobMGEvans

Pre-merge checks and finishing touches

❌ Failed checks (4 warnings)
Check name Status Explanation Resolution
Resource Jsdoc ⚠️ Warning The Hyperdrive resource JSDoc includes accurate descriptions of inputs and outputs, and contains 5 @example sections covering PostgreSQL, MySQL, caching, mTLS, and access credentials. However, the examples do not demonstrate the new local-development-only pattern introduced in this PR where the origin property is omitted and only dev.origin is provided. This represents a gap in documenting the new feature, as users reading the JSDoc would not see an example of the optional origin pattern that the PR enables. Add a new @example section to the Hyperdrive JSDoc that demonstrates the local-development-only pattern, showing how to create a Hyperdrive with only dev.origin and no production origin. This example should match the pattern shown in examples/cloudflare-dev-only-hyperdrive/alchemy.run.ts where origin is conditionally omitted in local mode and only dev.origin is provided.
Alchemy Style Resources ⚠️ Warning
Documentation ⚠️ Warning The documentation for Hyperdrive's local-only feature is incomplete and contains inconsistencies that violate the custom check criteria. While the Hyperdrive resource documentation does cover properties comprehensively, the accompanying example has critical issues: the README title and prerequisites reference "Prisma Postgres" but the actual worker implementation uses the raw PostgreSQL pg client without Prisma, creating documentation misalignment. The example code contains a production-critical bug (connection leak—client.end() is never called), violating production-readiness standards. The documentation lacks explicit calls-to-action and cross-references to related resources at the bottom. The input resource (Prisma Postgres Connection) should be minimally documented within the example context but isn't clearly explained. To pass this check, correct the example README to accurately describe the actual implementation (PostgreSQL with pg client, not Prisma), fix environment variable references to match what's actually needed, add await client.end() in a finally block in worker.ts to prevent connection leaks, and add a clear call-to-action section at the end of the Hyperdrive documentation with references to related resources such as database connection guides, Cloudflare Tunnel documentation, and production deployment best practices. Ensure the input resource (Connection) is clearly documented as part of the example setup.
Test Coverage ⚠️ Warning The PR adds one new test case "hyperdrive with no production origin throws" that validates error handling when deploying to production without an origin property, with proper cleanup using destroy(scope) in a finally block. However, test coverage is incomplete for the feature's intended use case. The PR enables local-only development by making origin optional with a fallback to dev.origin when in local scope, but the test suite only validates the negative case (error when deploying without origin in production scope), not the positive case where a Hyperdrive is successfully created and used with only dev.origin in local scope. While an example project demonstrates the pattern, it does not serve as an integration test that validates resource creation/destruction through control plane APIs as required by the test coverage criteria. Add a positive test case that successfully creates a Hyperdrive with only dev.origin (omitting the production origin) in local scope, verifies the resource was created through the Cloudflare API, and then cleans up via destroy(scope) in a finally block. This test should complement the existing error-case test and comprehensively validate that the new optional origin behavior works as intended for local-only development scenarios.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat(cloudflare): make Hyperdrive origin optional for local development" follows the conventional commits format with a valid type (feat), a defined scope (cloudflare), and a clear description. The title is fully aligned with the primary objective of the changeset, which is to make the origin property optional in HyperdriveProps to enable local-first development workflows. The changes throughout the PR—including the optional property modification, runtime validation logic, documentation updates, and supporting test cases—all directly support this core objective described in the title.

Comment @coderabbitai help to get the list of available commands and usage tips.

@Mkassabov
Copy link
Collaborator

@coderabbitai can you review this PR please?

@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

@Mkassabov: I'll review this PR for you. Let me analyze the changes to ensure the implementation is correct and the local development workflow improvement works as intended.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
alchemy/test/cloudflare/hyperdrive.test.ts (1)

171-188: Simplify test: NeonProject creation appears unnecessary.

The test creates a NeonProject but then uses its connection parameters only for dev.origin. Since the test validates that a production origin is required (and should fail without it), creating an actual Neon database seems wasteful. A simpler local connection string would suffice for dev.origin.

Consider simplifying the test to avoid unnecessary resource creation:

   test("hyperdrive with no production origin throws", async (scope) => {
     try {
-      const project = await NeonProject(`${testId}-dev-only`, {
-        name: `Hyperdrive Test Dev Only ${BRANCH_PREFIX}`,
-      });
-
       await expect(
         Hyperdrive(`${testId}-dev`, {
           name: `test-hyperdrive-dev-${BRANCH_PREFIX}`,
           dev: {
-            origin: project.connection_uris[0].connection_parameters,
+            origin: "postgres://postgres:postgres@localhost:5432/postgres",
           },
         }),
       ).rejects.toThrowError(/requires 'origin' for production deployment/);
     } finally {
       await destroy(scope);
     }
   });
examples/cloudflare-dev-only-hyperdrive/alchemy.run.ts (1)

16-21: Consider clarifying the example's purpose.

The example name "cloudflare-dev-only-hyperdrive" suggests a setup that only works locally (like the docs example with localhost:5432), but this example creates production infrastructure (PrismaPostgres) and conditionally omits the production origin in local mode.

While the code is correct, this pattern is more of a "progressive configuration" workflow rather than a true "dev-only" setup. Consider either:

  1. Renaming the example to better reflect what it demonstrates (e.g., cloudflare-hyperdrive-progressive-config)
  2. Changing the example to use a hardcoded localhost connection string like the docs show
  3. Adding a comment explaining this pattern is for teams using cloud databases in local development
alchemy/src/cloudflare/hyperdrive.ts (1)

317-340: Validate that at least one origin is provided.

The validation logic correctly handles the production case (line 321), but in local mode, if neither origin nor dev.origin is provided, the code will fail at runtime when trying to normalize undefined (line 332). This would result in an unclear error message.

Consider adding validation to ensure at least one is provided:

  // In local mode, origin can be omitted if dev.origin is provided
  const devOrigin = props.dev?.origin;
  const productionOrigin = props.origin;

  if (!Scope.current.local && !productionOrigin) {
    throw new Error(
      `Hyperdrive "${id}" requires 'origin' for production deployment. ` +
        `Add the production database connection to enable deployment.\n\n` +
        `For local development only, you can omit 'origin' and only provide 'dev.origin'.`,
    );
  }

+  // Ensure at least one origin is provided
+  if (!productionOrigin && !devOrigin) {
+    throw new Error(
+      `Hyperdrive "${id}" requires either 'origin' or 'dev.origin'. ` +
+        `Provide at least one database connection configuration.`,
+    );
+  }
+
  // Use dev.origin as fallback if origin is not provided (local mode only)
  const origin = productionOrigin
    ? normalizeHyperdriveOrigin(productionOrigin)
    : normalizeHyperdriveOrigin(devOrigin!);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2432155 and 28f033f.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • alchemy-web/src/content/docs/providers/cloudflare/hyperdrive.md (1 hunks)
  • alchemy/src/cloudflare/hyperdrive.ts (2 hunks)
  • alchemy/test/cloudflare/hyperdrive.test.ts (1 hunks)
  • examples/cloudflare-dev-only-hyperdrive/README.md (1 hunks)
  • examples/cloudflare-dev-only-hyperdrive/alchemy.run.ts (1 hunks)
  • examples/cloudflare-dev-only-hyperdrive/package.json (1 hunks)
  • examples/cloudflare-dev-only-hyperdrive/src/worker.ts (1 hunks)
  • examples/cloudflare-dev-only-hyperdrive/tsconfig.json (1 hunks)
  • examples/cloudflare-tanstack-start/src/routeTree.gen.ts (2 hunks)
  • package.json (1 hunks)
  • tests/smoke-test-flatten-website/package.json (2 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-10-19T21:11:37.829Z
Learnt from: CR
PR: alchemy-run/alchemy#0
File: alchemy/templates/rwsdk/.cursor/rules/alchemy_cloudflare.mdc:0-0
Timestamp: 2025-10-19T21:11:37.829Z
Learning: Applies to alchemy/templates/rwsdk/**/worker.ts : Type worker implementations with env: typeof worker.Env in the default export’s fetch handler

Applied to files:

  • examples/cloudflare-dev-only-hyperdrive/src/worker.ts
📚 Learning: 2025-10-19T21:13:28.624Z
Learnt from: CR
PR: alchemy-run/alchemy#0
File: alchemy/templates/rwsdk/.cursor/rules/rwsdk_rwsdk-request-response.mdc:0-0
Timestamp: 2025-10-19T21:13:28.624Z
Learning: Applies to alchemy/templates/rwsdk/src/worker.tsx : Import co-located routes into defineApp using prefix()

Applied to files:

  • examples/cloudflare-tanstack-start/src/routeTree.gen.ts
📚 Learning: 2025-10-19T21:10:23.073Z
Learnt from: CR
PR: alchemy-run/alchemy#0
File: .cursor/rules/cloudflare.mdc:0-0
Timestamp: 2025-10-19T21:10:23.073Z
Learning: Applies to alchemy/src/cloudflare/!{bindings,bound,worker}.ts : Create a new resource file at alchemy/src/cloudflare/{resource}.ts for the added resource

Applied to files:

  • examples/cloudflare-dev-only-hyperdrive/tsconfig.json
  • examples/cloudflare-dev-only-hyperdrive/alchemy.run.ts
📚 Learning: 2025-10-19T23:34:43.575Z
Learnt from: CR
PR: alchemy-run/alchemy#0
File: AGENTS.md:0-0
Timestamp: 2025-10-19T23:34:43.575Z
Learning: Applies to examples/*/tsconfig.json : Each example project’s tsconfig.json must extend ../../tsconfig.base.json

Applied to files:

  • examples/cloudflare-dev-only-hyperdrive/tsconfig.json
📚 Learning: 2025-10-19T23:34:43.575Z
Learnt from: CR
PR: alchemy-run/alchemy#0
File: AGENTS.md:0-0
Timestamp: 2025-10-19T23:34:43.575Z
Learning: Applies to examples/*/alchemy.run.ts : Each example must include an alchemy.run.ts used to deploy and tear down resources

Applied to files:

  • examples/cloudflare-dev-only-hyperdrive/tsconfig.json
  • examples/cloudflare-dev-only-hyperdrive/alchemy.run.ts
📚 Learning: 2025-10-19T21:10:23.073Z
Learnt from: CR
PR: alchemy-run/alchemy#0
File: .cursor/rules/cloudflare.mdc:0-0
Timestamp: 2025-10-19T21:10:23.073Z
Learning: Applies to alchemy/src/cloudflare/worker.ts : Update alchemy/src/cloudflare/worker.ts to map the new binding to the Cloudflare metadata API

Applied to files:

  • examples/cloudflare-dev-only-hyperdrive/alchemy.run.ts
🔇 Additional comments (7)
tests/smoke-test-flatten-website/package.json (1)

20-20: Verify the version pinning strategy for this smoke test.

The alchemy dependency was changed from "workspace:*" to "0.62.2", which pins to a specific version rather than using the workspace protocol. In a monorepo, smoke tests typically use "workspace:*" to test against the current local version of the package.

Is this version pinning intentional? If so, please clarify the reasoning. Otherwise, consider reverting to "workspace:*" to ensure the smoke test runs against the local development version.

examples/cloudflare-dev-only-hyperdrive/package.json (1)

1-18: LGTM!

The package structure follows monorepo conventions correctly, using workspace:* for the alchemy dependency and catalog: for typescript.

examples/cloudflare-tanstack-start/src/routeTree.gen.ts (1)

1-177: Auto-generated file - no review needed.

This file is explicitly marked as auto-generated by TanStack Router (lines 7-9) and contains only formatting changes (quote style). Auto-generated files should not be manually reviewed.

examples/cloudflare-dev-only-hyperdrive/tsconfig.json (1)

1-8: LGTM!

The TypeScript configuration correctly extends the base config and follows the established pattern for example projects.

Based on learnings.

alchemy-web/src/content/docs/providers/cloudflare/hyperdrive.md (1)

41-56: Clear documentation of the new local-first development pattern.

The new section effectively explains how to use Hyperdrive for local development without production credentials. The example is straightforward, and the caution note appropriately warns developers about production requirements.

alchemy/src/cloudflare/hyperdrive.ts (2)

154-158: Well-documented interface change.

The updated comments clearly explain that origin is optional in local mode but required for production deployments. This aligns with the implementation logic.


313-346: Backward compatible implementation.

The changes maintain backward compatibility - existing code that provides origin continues to work as before. The new optional behavior enables local-first development workflows without breaking existing deployments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants