Cloudflare Pages mit Claude Code: Edge-Deployment 2026
Functions, D1 SQLite, KV Storage, R2 Object Storage, Wrangler — Claude Code deployt Full-Stack-Apps ans globale Edge-Netzwerk mit minimaler Latenz.
📅 6. Mai 2026⏱ 11 min Lesezeit🔥 Cloudflare, Claude Code, TypeScript
Cloudflare Pages ist 2026 weit mehr als ein statisches Hosting. Mit Pages Functions, D1-Datenbanken, KV-Storage und R2 Object Storage lassen sich vollständige Full-Stack-Applikationen direkt an Cloudflares globalem Edge-Netzwerk mit über 310 Rechenzentren deployen. Die Latenzen liegen oft unter 20 ms — ohne teuren Origin-Server.
Claude Code verändert diesen Prozess fundamental: Statt mühsam Dokumentation zu lesen, Konfigurationsdateien von Hand zu schreiben und Deployment-Fehler zu debuggen, beschreibt man einfach, was man will — und Claude Code generiert wrangler.toml, TypeScript-Functions, Migrations-SQL und GitHub-Actions-Pipelines in Sekunden.
Dieser Artikel zeigt, wie Claude Code und Cloudflare Pages zusammen eine moderne Full-Stack-Deployment-Pipeline ergeben — von der ersten Konfiguration bis zu automatisierten CI/CD-Flows mit Custom Domains.
⚡
Edge-first
310+ PoPs weltweit, <20 ms Latenz, kein Origin-Server nötig
📊
D1 SQLite
Serverlose relationale Datenbank direkt am Edge, automatische Replikation
🔑
KV Storage
Global verteilter Key-Value-Store, <1 ms Read-Latenz nach Write
💾
R2 Storage
S3-kompatibles Object Storage ohne Egress-Kosten
1. Cloudflare Pages Grundlagen
Pages
Cloudflare Pages ist Cloudflares JAMstack-Hosting-Plattform, die seit 2024 vollständig mit dem Workers-Ökosystem zusammengewachsen ist. Jedes Deployment erzeugt automatisch eine Vorschau-URL, Git-Integrationen in GitHub und GitLab sind nativer Bestandteil — und mit Wrangler lässt sich alles auch über die CLI steuern.
Was Claude Code für den Setup tut:
Erstellt wrangler.toml mit allen nötigen Bindings
Richtet package.json Build-Scripts ein
Generiert TypeScript-Konfiguration für Cloudflare-Typen
Schreibt initiale Functions und Tests
Erstellt GitHub Actions Workflow für automatisches Deployment
wrangler.toml — Das Herzstück
Die wrangler.toml ist das zentrale Konfigurationsfile für jede Cloudflare Pages-App. Claude Code erstellt sie vollständig konfiguriert mit allen benötigten Ressourcen-Bindings:
Das erste Deployment ist mit Claude Code ein Einzeiler-Prozess. Claude Code führt den Build durch, erstellt das Cloudflare-Projekt falls nötig und deployt direkt:
Terminal
# Wrangler installieren (falls noch nicht vorhanden)
npm install -g wrangler@latest
# Einloggen bei Cloudflare
wrangler login
# Projekt bauen (z.B. mit Vite, Next.js, Astro, etc.)
npm run build
# Erstes Deployment — Claude Code nutzt diesen Befehl
npx wrangler pages deploy ./dist \
--project-name=meine-app \
--commit-message="Initial deployment via Claude Code"
# Output:✨ Uploading 47 files
✨ Deployment complete!
🌎 https://meine-app.pages.dev
🌎 https://a1b2c3.meine-app.pages.dev (Vorschau-URL)
Git-Integration & Preview URLs
Cloudflare Pages erstellt für jeden Branch und jeden Pull Request automatisch eine Preview-URL. Claude Code kann diese Architektur direkt über die Cloudflare API konfigurieren — Production-Branch ist main, alle anderen Branches erhalten isolierte Preview-Umgebungen.
PR-Preview: Direkt im GitHub Pull Request verlinkt
Claude Code Tipp:
Claude Code merkt sich den State des Deployments und kann nach dem Deploy automatisch die Preview-URL aufrufen und mit Playwright testen, ob alle Endpoints korrekt antworten.
2. Pages Functions & Middleware
Functions
Pages Functions sind serverlose TypeScript/JavaScript-Funktionen, die direkt an Cloudflares Edge laufen — ohne Cold Starts, ohne Container, ohne Infrastruktur-Management. Das Routing ist dateibasiert im functions/-Verzeichnis.
Dateibasiertes Routing
Jede Datei im functions/-Verzeichnis wird zu einem API-Endpoint. Claude Code versteht dieses Routing-Schema und erstellt Functions mit korrekter TypeScript-Typisierung:
Routing-Struktur:
functions/api/users.ts → /api/users
functions/api/users/[id].ts → /api/users/:id
functions/api/[[catchall]].ts → /api/*
functions/_middleware.ts → Läuft vor allen Functions
functions/api/users/[id].ts
import type { PagesFunction } from '@cloudflare/workers-types';
// Typdefinition für alle Bindings (generiert von Claude Code)interfaceEnv {
DB: D1Database;
CACHE: KVNamespace;
STORAGE: R2Bucket;
AUTH_SECRET: string;
}
// GET /api/users/:id — Einzelnen User aus D1 ladenexport constonRequestGet: PagesFunction<Env> = async (context) => {
const { id } = context.params;
const { DB, CACHE } = context.env;
// Cache-First: KV prüfen bevor D1 Queryconst cached = await CACHE.get(`user:${id}`, { type: 'json' });
if (cached) {
return Response.json({ data: cached, source: 'cache' });
}
// D1 Queryconst stmt = DB.prepare('SELECT id, name, email, created_at FROM users WHERE id = ?');
const user = await stmt.bind(id).first();
if (!user) {
return Response.json({ error: 'User nicht gefunden' }, { status: 404 });
}
// In KV cachen (TTL: 5 Minuten)await CACHE.put(`user:${id}`, JSON.stringify(user), { expirationTtl: 300 });
return Response.json({ data: user, source: 'db' });
};
// PUT /api/users/:id — User updatenexport constonRequestPut: PagesFunction<Env> = async (context) => {
const { id } = context.params;
const body = await context.request.json<{ name?: string; email?: string }>();
if (!body.name && !body.email) {
return Response.json({ error: 'Keine Änderungen übermittelt' }, { status: 400 });
}
await context.env.DB
.prepare('UPDATE users SET name = COALESCE(?, name), email = COALESCE(?, email) WHERE id = ?')
.bind(body.name ?? null, body.email ?? null, id)
.run();
// Cache invalidieren nach Updateawait context.env.CACHE.delete(`user:${id}`);
return Response.json({ success: true, updated: id });
};
// DELETE /api/users/:id — User löschenexport constonRequestDelete: PagesFunction<Env> = async (context) => {
const { id } = context.params;
const authHeader = context.request.headers.get('Authorization');
// Auth-Check via Secretif (authHeader !== `Bearer ${context.env.AUTH_SECRET}`) {
return Response.json({ error: 'Nicht autorisiert' }, { status: 401 });
}
await context.env.DB.prepare('DELETE FROM users WHERE id = ?').bind(id).run();
await context.env.CACHE.delete(`user:${id}`);
return Response.json({ success: true, deleted: id });
};
Middleware-Pattern
Middleware in Pages Functions läuft vor allen anderen Functions. Claude Code erstellt Middleware-Dateien für Authentication, Logging, Rate Limiting und CORS automatisch:
functions/_middleware.ts
import type { PagesFunction } from '@cloudflare/workers-types';
import type { Env } from './types';
// CORS-Middleware — läuft vor allen API-EndpunktenconstcorsMiddleware: PagesFunction<Env> = async (context) => {
const response = await context.next();
const headers = new Headers(response.headers);
headers.set('Access-Control-Allow-Origin', 'https://meine-app.com');
headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return new Response(response.body, { ...response, headers });
};
// Rate-Limiting-Middleware via KVconstrateLimitMiddleware: PagesFunction<Env> = async (context) => {
const ip = context.request.headers.get('CF-Connecting-IP') ?? 'unknown';
const key = `ratelimit:${ip}`;
const limit = 100; // Requests pro Minuteconst current = await context.env.CACHE.get(key);
const count = current ? parseInt(current) : 0;
if (count >= limit) {
return Response.json(
{ error: 'Rate limit überschritten', retryAfter: 60 },
{ status: 429, headers: { 'Retry-After': '60' } }
);
}
await context.env.CACHE.put(key, String(count + 1), { expirationTtl: 60 });
return context.next();
};
// Middleware-Kette exportierenexport constonRequest = [corsMiddleware, rateLimitMiddleware];
Wichtig:
Middleware im functions/-Root läuft für alle Routes. Unterordner können eigene _middleware.ts haben, die nur für diesen Unterbereich gilt — zum Beispiel Auth nur für /api/admin/*.
3. D1 Datenbank
D1 SQLite
D1 ist Cloudflares serverlose SQLite-Datenbank, die direkt am Edge läuft. Seit dem GA-Release 2025 unterstützt D1 read replicas in allen Regionen, sodass Reads immer vom nächstgelegenen PoP beantwortet werden.
D1 Datenbank erstellen und verbinden
Terminal
# D1 Datenbank erstellen
wrangler d1 create meine-app-db
# Output enthält die database_id für wrangler.toml:✅ Successfully created DB 'meine-app-db' in region WEUR
Created your new D1 database.
[[d1_databases]]
binding = "DB"
database_name = "meine-app-db"
database_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"# SQL direkt ausführen (für Tests)
wrangler d1 execute meine-app-db --command="SELECT sqlite_version()"
# Migration aus Datei ausführen
wrangler d1 execute meine-app-db --file=./migrations/0001_initial.sql
# Migrations mit wrangler d1 migrations (automatisches Tracking)
wrangler d1 migrations apply meine-app-db
SQL-Migrationen
Claude Code erstellt SQL-Migrationen vollständig typsicher und mit allen nötigen Indizes. Das Migrations-System von D1 trackt, welche Migrationen bereits angewandt wurden:
migrations/0001_initial.sql
-- Migration 0001: Initiales Schema — generiert von Claude Code-- D1 nutzt SQLite-Syntax mit Cloudflare-Erweiterungen
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
plan TEXT NOT NULL DEFAULT 'free' CHECK (plan IN ('free', 'pro', 'enterprise')),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_plan ON users(plan);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash TEXT NOT NULL UNIQUE,
expires_at DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_sessions_user_id ON sessions(user_id);
CREATE INDEX idx_sessions_token ON sessions(token_hash);
-- Trigger: updated_at automatisch setzen
CREATE TRIGGER update_users_timestamp
AFTER UPDATE ON users
FOR EACH ROW BEGIN
UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE id = OLD.id;
END;
D1 mit Drizzle ORM
Für typsichere Datenbankzugriffe empfiehlt Claude Code Drizzle ORM, das nativen D1-Support bietet:
import { drizzle } from 'drizzle-orm/d1';
import { users } from '../../src/db/schema';
import { eq, desc } from 'drizzle-orm';
import type { PagesFunction } from '@cloudflare/workers-types';
import type { Env } from './types';
export constonRequestGet: PagesFunction<Env> = async (context) => {
const db = drizzle(context.env.DB);
const url = new URL(context.request.url);
const limit = parseInt(url.searchParams.get('limit') ?? '20');
const offset = parseInt(url.searchParams.get('offset') ?? '0');
const result = await db
.select({ id: users.id, name: users.name, email: users.email, plan: users.plan })
.from(users)
.orderBy(desc(users.createdAt))
.limit(limit)
.offset(offset);
return Response.json({ data: result, limit, offset });
};
D1 Performance-Tipp:
Batch-Queries mit db.batch() sparen Round-Trips. Für komplexe Reports nutze prepare().bind().all() und kombiniere das mit KV-Caching für häufige Aggregationen.
4. KV Storage
KV
Cloudflare KV ist ein global verteilter Key-Value-Store. Writes propagieren innerhalb von ~60 Sekunden, Reads sind immer lokal am Edge (<1 ms). KV eignet sich für Caching, Session-Storage, Feature-Flags und häufig gelesene Konfiguration.
KV Namespace erstellen
Terminal
# KV Namespace für Production erstellen
wrangler kv namespace create CACHE
# Output für wrangler.toml:✅ Successfully created namespace
Add the following to your wrangler.toml:
[[kv_namespaces]]
binding = "CACHE"
id = "abcdef1234567890abcdef1234567890"# Preview-Namespace für lokale Entwicklung
wrangler kv namespace create CACHE --preview
# Werte direkt über CLI setzen (für Tests)
wrangler kv key put "feature:new-dashboard" "true" --namespace-id=abcdef...
wrangler kv key get "feature:new-dashboard" --namespace-id=abcdef...
# Alle Keys auflisten
wrangler kv key list --namespace-id=abcdef...
KV API in Functions
Claude Code generiert KV-Zugriffe mit korrektem Typing und optimierten Mustern für Caching und Session-Management:
Cloudflare R2 ist S3-kompatibles Object Storage ohne Egress-Gebühren. Für Upload-heavy Apps (User-Avatare, Dokumente, Media-Files) ist R2 oft günstiger als S3 + CloudFront. Claude Code erstellt vollständige Upload-Pipelines mit Presigned URLs und direktem Browser-Upload.
Das sichere Pattern für Browser-Uploads: Browser fragt nach Presigned URL, lädt direkt zu R2 hoch — kein Traffic über Functions, keine Datei-Limits durch den Worker:
// R2-Objekte über Pages Functions ausliefern (mit Cache-Control)export constonRequestGet: PagesFunction<Env> = async (context) => {
const path = (context.params.path as string[]).join('/');
const obj = await context.env.STORAGE.get(path);
if (!obj) {
return new Response('Not Found', { status: 404 });
}
const headers = new Headers();
obj.writeHttpMetadata(headers);
headers.set('Cache-Control', 'public, max-age=31536000, immutable');
headers.set('ETag', obj.httpEtag);
// Conditional Request Supportconst ifNoneMatch = context.request.headers.get('If-None-Match');
if (ifNoneMatch === obj.httpEtag) {
return new Response(null, { status: 304, headers });
}
return new Response(obj.body, { headers });
};
6. Wrangler & CI/CD
CI/CD
Cloudflare Pages CI/CD-Integration ist mit Wrangler und GitHub Actions vollautomatisiert. Claude Code erstellt komplette Pipeline-Dateien, die bei jedem Push auf main deployen und bei Pull Requests Preview-URLs erzeugen.
Wrangler Environments
Mit Environments in wrangler.toml lassen sich Production, Staging und Preview-Umgebungen mit unterschiedlichen Bindings und Variablen konfigurieren:
Custom Domains werden über das Cloudflare Dashboard oder die API verknüpft. Mit einer _headers-Datei im Build-Output setzt Claude Code Security-Header:
public/_headers
# Cloudflare Pages Headers — generiert von Claude Code# Gilt für alle Seiten
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.meine-app.com
# Assets: lange Cache-Time, immutable
/assets/*
Cache-Control: public, max-age=31536000, immutable
# API: kein Caching
/api/*
Cache-Control: no-store, no-cache, must-revalidate
Vollständiger Claude Code Workflow:
Claude Code analysiert die bestehende App-Architektur
Generiert wrangler.toml mit allen Bindings für D1, KV, R2
Erstellt TypeScript-Types für alle Environments
Schreibt Pages Functions mit vollständiger Fehlerbehandlung
Erstellt SQL-Migrations und Drizzle-Schema
Generiert GitHub Actions Workflow
Führt erstes Deployment aus und verifiziert alle Endpoints
Secrets sicher verwalten:
wrangler secret put AUTH_SECRET — Secrets werden verschlüsselt gespeichert und sind NUR in der Runtime verfügbar, niemals in wrangler.toml oder Logs. Claude Code erinnert dich daran, alle Secrets vor dem ersten Deployment zu setzen.
Lokale Entwicklung mit wrangler dev
Terminal
# Lokaler Dev-Server mit echten Edge-Bindings
wrangler pages dev ./dist \
--d1=DB:meine-app-db \
--kv=CACHE:preview-kv-id \
--r2=STORAGE:meine-app-uploads \
--port=8788
# Oder via npm script (empfohlen von Claude Code):# package.json: "dev": "vite build --watch & wrangler pages dev ./dist"
npm run dev
# D1-Tabellen lokal abfragen
wrangler d1 execute meine-app-db --local --command="SELECT COUNT(*) FROM users"
# KV-Werte lokal setzen
wrangler kv key put "feature:beta" "true" --binding=CACHE --local
Claude Code + Wrangler dev:
Claude Code startet wrangler pages dev automatisch, führt Changes sofort durch, testet alle API-Endpoints und gibt direkt Feedback — ohne manuelles Terminal-Wechseln.
Edge-Deployment mit Claude Code ausprobieren
Deploye deine nächste Full-Stack-App mit Cloudflare Pages und Claude Code — D1, KV, R2 inklusive, in Minuten produktionsreif.