Claude Code Plugins

Community-maintained marketplace

Feedback

alchemy-cloudflare

@wodsmith/thewodapp
0
0

Alchemy IaC patterns for deploying TanStack Start apps to Cloudflare Workers with D1 databases. Use when setting up new TanStack Start projects, configuring Alchemy deployments, working with D1/Drizzle migrations, local development with Cloudflare bindings, or deploying to custom domains.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name alchemy-cloudflare
description Alchemy IaC patterns for deploying TanStack Start apps to Cloudflare Workers with D1 databases. Use when setting up new TanStack Start projects, configuring Alchemy deployments, working with D1/Drizzle migrations, local development with Cloudflare bindings, or deploying to custom domains.

Alchemy Cloudflare IaC

TypeScript-first Infrastructure as Code for deploying TanStack Start applications to Cloudflare Workers.

Core Concepts

  • alchemy.run.ts: Infrastructure definition file (TypeScript, not YAML)
  • TanStackStart resource: Wraps Worker config specifically for TanStack builds
  • D1Database resource: Manages D1 with automatic Drizzle migration application
  • Type inference: typeof worker.Env provides types without codegen
  • Secrets: alchemy.secret() encrypts values with ALCHEMY_PASSWORD

Quick Start

1. Install Dependencies

pnpm add alchemy @cloudflare/workers-types

2. Create alchemy.run.ts

import alchemy from "alchemy"
import { D1Database, TanStackStart } from "alchemy/cloudflare"

const app = await alchemy("my-app", {
  stage: process.env.STAGE ?? "dev",
  phase: process.argv.includes("--destroy") ? "destroy" : "up",
})

const db = await D1Database("my-d1", {
  migrationsDir: "./drizzle",  // Auto-applies Drizzle migrations
})

const worker = await TanStackStart("my-worker", {
  d1Databases: { DB: db },
  domains: ["my-app.com"],  // Custom domain
})

export type Env = typeof worker.Env

await app.finalize()

3. Configure Vite

CRITICAL: Alchemy plugin must be FIRST in plugins array.

// app.config.ts
import { defineConfig } from "@tanstack/react-start/config"
import viteTsConfigPaths from "vite-tsconfig-paths"
import { alchemy } from "alchemy/cloudflare/tanstack-start"

export default defineConfig({
  vite: {
    plugins: [
      alchemy(),  // MUST be first
      viteTsConfigPaths({ root: "./" }),
    ],
    build: {
      target: "esnext",
      rollupOptions: {
        external: ["node:async_hooks", "cloudflare:workers"],
      },
    },
  },
})

4. Deploy

# Set encryption password (once)
export ALCHEMY_PASSWORD="your-secure-password"

# Deploy
bun alchemy.run.ts

# Deploy to specific stage
STAGE=prod bun alchemy.run.ts

# Destroy resources
bun alchemy.run.ts --destroy

Local Development

# Run dev server with Cloudflare emulation
pnpm alchemy dev

What this provides:

  • Full D1 database emulation (persisted in .alchemy/{app}/{stage}/)
  • KV, R2, Durable Objects bindings
  • Same Env types as production

Only required env var:

ALCHEMY_PASSWORD=your-password

D1 + Drizzle Integration

Migration Workflow

  1. Modify schema in src/db/schema.ts
  2. Generate migration: pnpm drizzle-kit generate
  3. Deploy: bun alchemy.run.ts (auto-applies migrations)

Accessing D1

// In server functions or loaders
import { getCloudflareContext } from "@opennextjs/cloudflare"

export async function loader() {
  const { env } = await getCloudflareContext()
  const db = drizzle(env.DB)
  // Use db...
}

Common Patterns

Multiple Environments

const stage = process.env.STAGE ?? "dev"

const app = await alchemy("my-app", { stage })

// Conditional resources
const domains = stage === "prod" 
  ? ["app.com", "www.app.com"]
  : [`${stage}.app.com`]

await TanStackStart("worker", { domains })

Secrets Management

// In alchemy.run.ts
const worker = await TanStackStart("worker", {
  vars: {
    PUBLIC_API_URL: "https://api.example.com",
  },
  secretTextBindings: {
    AUTH_SECRET: alchemy.secret(process.env.AUTH_SECRET!),
    STRIPE_KEY: alchemy.secret(process.env.STRIPE_KEY!),
  },
})

KV Namespace

import { KVNamespace, TanStackStart } from "alchemy/cloudflare"

const sessions = await KVNamespace("sessions")

await TanStackStart("worker", {
  kvNamespaces: { SESSIONS: sessions },
})

Troubleshooting

"cloudflare:workers" resolve error

Add to vite config:

rollupOptions: {
  external: ["node:async_hooks", "cloudflare:workers"],
}

"Route files should not import @/db"

Server functions must be in src/server-fns/ files, not inline in route files. Routes can only import and call server functions.

D1 not persisting locally

Check .alchemy/{app}/{stage}/ directory exists. Ensure ALCHEMY_PASSWORD is set.

Migration not applying

Verify migrationsDir points to correct directory (where .sql files are).

References