Skip to content

fix(cloudflare): Json() bindings deploy as native types instead of strings - #1451

Merged
sam-goodwin merged 2 commits into
mainfrom
claude/issue-1450-15802a
Jul 12, 2026
Merged

fix(cloudflare): Json() bindings deploy as native types instead of strings#1451
sam-goodwin merged 2 commits into
mainfrom
claude/issue-1450-15802a

Conversation

@sam-goodwin

@sam-goodwin sam-goodwin commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Closes #1450

Problem

Json(value) bindings behaved differently between local dev and production:

  • Local dev: miniflare received the raw value, so env.IS_DEVELOPMENT was a native boolean.
  • Production: prepareWorkerMetadata called JSON.stringify(binding.json) before sending the binding to the Cloudflare API, so the runtime injected the JSON string ("false") instead of the native value (false). Since Boolean("false") === true, this silently broke conditional checks in production.

Fix

Send the raw value in the json field of the worker metadata binding, exactly as wrangler does (wrangler converts non-string vars to { type: "json", name, json: value } with the raw parsed value — see create-worker-upload-form.ts). The whole metadata object is JSON.stringify-ed once when appended to the upload form, so the value serializes correctly without double encoding.

Also updated WorkerBindingJson.json from string to unknown to match.

Usage

import { Worker, Json } from "alchemy/cloudflare";

const worker = await Worker("api", {
  entrypoint: "./src/worker.ts",
  bindings: {
    IS_DEVELOPMENT: Json(false),
    MAX_RETRIES: Json(3),
    FEATURES: Json({ beta: true }),
  },
});
// src/worker.ts — now identical in local dev and production:
export default {
  async fetch(request, env) {
    console.log(typeof env.IS_DEVELOPMENT, env.IS_DEVELOPMENT); // "boolean" false
    if (env.IS_DEVELOPMENT) {
      // no longer incorrectly taken in production
    }
    return new Response("ok");
  },
};

Test

Added "json bindings are injected as native types" to worker.test.ts: deploys a worker with boolean, number, and object Json bindings and asserts the deployed worker observes the native types (typeof env.IS_DEV === "boolean", etc.) via its workers.dev URL.

🤖 Generated with Claude Code

…ts native types

The Cloudflare API expects the raw JSON value in the `json` field of a
json binding (as wrangler sends it). Stringifying it first made the
runtime inject a JSON *string* (e.g. "false") instead of the native
value (false), diverging from local dev where miniflare received the
raw value.

Fixes #1450

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Jul 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/alchemy@1451

commit: 487e773

The Artifacts service now returns account-scoped remote URLs
(https://<account-hash>.artifacts.cloudflare.net/git/<ns>/<repo>.git)
and ARTIFACTS.get() returns a JS-RPC repo handle whose metadata is
exposed via info() rather than as plain data properties.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sam-goodwin
sam-goodwin merged commit c27b340 into main Jul 12, 2026
4 of 6 checks passed
@sam-goodwin
sam-goodwin deleted the claude/issue-1450-15802a branch July 12, 2026 23:13
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.

Bug: Json() binding evaluates to string in production instead of native boolean type

1 participant