跳到內容

ch02-toolchain

取得並執行

這個範例可以獨立 clone 執行,不依賴其他章節。

npx degit mrsuner/cloudflare-serverless-platform-manuals/examples/ch02-toolchain
cd ch02-toolchain
npm install

可用指令

npm run dev	# wrangler dev
npm run deploy	# wrangler deploy
npm run typecheck	# tsc --noEmit
npm run cf-typegen	# wrangler types --env-interface CloudflareBindings

說明

ch02 — Toolchain: config, types, bindings, environments

Section titled “ch02 — Toolchain: config, types, bindings, environments”

Companion example for docs/02-toolchain.md.

One Worker wired to a var, a secret, a KV binding, and two environments — enough to demonstrate every part of the 2026 Wrangler workflow.

Terminal window
npm install
echo 'API_KEY="local-dev-key-not-a-real-secret"' > .dev.vars
npm run cf-typegen
# Local KV write. No login, no --local flag needed on Wrangler v4.
npx wrangler kv key put greeting "hello from local KV" --binding SETTINGS
npm run dev
Terminal window
$ curl -s localhost:8787/config
{"appName":"ch02-toolchain","tier":"dev","hasApiKey":true}
$ curl -s localhost:8787/settings
{"greeting":"hello from local KV"}

Now the other environment:

Terminal window
$ npx wrangler dev --env staging
$ curl -s localhost:8787/config
{"appName":"ch02-toolchain","tier":"staging","hasApiKey":true}
$ curl -s localhost:8787/settings
{"greeting":null}

greeting is null under staging because that environment binds a different KV namespace id, and local state is partitioned by id.

Everything above runs offline. So does config validation:

Terminal window
npx wrangler deploy --dry-run --outdir out
npx wrangler types --check

Tested 2026-07-28 with wrangler@4.114.0, workerd@1.20260722.1.

1. compatibility_date changes bundle size by 117×

Section titled “1. compatibility_date changes bundle size by 117×”

Identical source importing node:fs, both with nodejs_compat:

compatibility_dateUploadgzipWhy
2025-01-0125.30 KiB5.88 KiBunenv polyfill bundled (32 references)
2026-07-240.22 KiB0.17 KiBnode:fs native since 2025-09-15

2. compatibility_date changes the generated type surface

Section titled “2. compatibility_date changes the generated type surface”
compatibility_dateworker-configuration.d.ts
2026-07-2414,716 lines
2022-01-0114,576 lines

Missing at the older date: Navigator, MessageChannel, ReadableByteStreamController, ReadableStreamBYOBRequest, TransformStreamDefaultController, WritableStreamDefaultController.

3. wrangler types reads .dev.vars and literal-types your vars

Section titled “3. wrangler types reads .dev.vars and literal-types your vars”
interface __BaseEnv_CloudflareBindings {
SETTINGS: KVNamespace;
APP_NAME: "ch02-toolchain"; // literal, not string
TIER: "staging" | "dev"; // union across ALL environments
API_KEY: string; // picked up from .dev.vars
}

env.TIER === "production" is therefore a compile error.

Declaring only env.staging.vars.TIER drops APP_NAME and SETTINGS entirely — with a warning, not an error, so the deploy succeeds and the Worker breaks at runtime:

▲ [WARNING] "vars" configuration is not inherited by environments.
▲ [WARNING] "kv_namespaces" exists at the top level, but not on "env.staging".
Your Worker has access to the following bindings:
Binding Resource
env.TIER ("staging") Environment Variable

Repeat every var and every binding in every named environment. compatibility_date, compatibility_flags and main are inherited.

原始碼

wrangler.jsonc

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "ch02-toolchain",
  "main": "src/index.ts",
  "compatibility_date": "2026-07-24",
  "observability": { "enabled": true },

  "vars": { "APP_NAME": "ch02-toolchain", "TIER": "dev" },

  // The id is arbitrary for local development — it is only used to
  // partition local state. A real id is needed to talk to production.
  "kv_namespaces": [
    { "binding": "SETTINGS", "id": "0000000000000000000000000000ffff" }
  ],

  // Environments do NOT inherit vars or bindings.
  // Everything must be repeated here, or it silently disappears.
  "env": {
    "staging": {
      "vars": { "APP_NAME": "ch02-toolchain", "TIER": "staging" },
      "kv_namespaces": [
        { "binding": "SETTINGS", "id": "0000000000000000000000000000eeee" }
      ]
    }
  }
}

package.json

{
  "name": "ch02-toolchain",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "wrangler dev",
    "deploy": "wrangler deploy",
    "typecheck": "tsc --noEmit",
    "cf-typegen": "wrangler types --env-interface CloudflareBindings"
  },
  "devDependencies": { "typescript": "^5.9.0", "wrangler": "^4.114.0" }
}

src/index.ts

import { env } from "cloudflare:workers";

// Top-level access to vars and secrets is allowed.
// Top-level I/O is NOT — see chapter 01.
const APP = env.APP_NAME;

export default {
  async fetch(request: Request, workerEnv: CloudflareBindings): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/config") {
      return Response.json({
        appName: APP,
        tier: workerEnv.TIER,
        // Never return a secret. We only prove it was injected.
        hasApiKey: typeof workerEnv.API_KEY === "string" && workerEnv.API_KEY.length > 0,
      });
    }

    if (url.pathname === "/settings") {
      const value = await workerEnv.SETTINGS.get("greeting");
      return Response.json({ greeting: value ?? null });
    }

    return new Response("Try /config or /settings\n", { status: 404 });
  },
} satisfies ExportedHandler<CloudflareBindings>;

.dev.vars.example

API_KEY="local-dev-key-not-a-real-secret"

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext", "lib": ["esnext"], "module": "esnext",
    "moduleResolution": "bundler", "types": ["./worker-configuration.d.ts"],
    "strict": true, "skipLibCheck": true, "noEmit": true, "isolatedModules": true
  },
  "include": ["src/**/*.ts", "worker-configuration.d.ts"]
}