alchemy-cloudflare

wodsmith's avatarfrom wodsmith

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.

⭐0stars🔀1forks📁View on GitHub🕐Updated Jan 3, 2026⬇️2downloads

When & Why to Use This Skill

This Claude skill streamlines the deployment of TanStack Start applications to Cloudflare Workers using Alchemy's TypeScript-first Infrastructure as Code (IaC) patterns. It eliminates the need for complex YAML configurations by providing a type-safe way to manage Cloudflare resources, including D1 databases, KV namespaces, and custom domains, while ensuring seamless integration with Drizzle ORM migrations.

Use Cases

  • Provisioning and deploying full-stack TanStack Start applications to Cloudflare Workers with a single TypeScript command.
  • Automating D1 database setup and synchronizing Drizzle migrations during the CI/CD deployment process.
  • Configuring multi-stage environments (e.g., dev, staging, prod) with conditional resource bindings and custom domain mapping.
  • Setting up local development environments that accurately emulate Cloudflare services like D1, KV, and R2 for high-fidelity testing.
  • Managing sensitive environment variables and secrets using encrypted Alchemy patterns to enhance infrastructure security.
namealchemy-cloudflare
descriptionAlchemy 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