Cloudflare D1: SQLite-Datenbank direkt in Cloudflare Workers integrieren

Cloudflare Workers sind längst nicht mehr auf zustandslose HTTP-Handler beschränkt. Mit Cloudflare D1 bietet Cloudflare eine eigene SQLite-Datenbank an, die tief in das Workers-Ökosystem integriert ist. Kein externer Datenbankserver, keine Netzwerk-Roundtrips zu fremden Rechenzentren – D1 läuft direkt im Cloudflare-Netzwerk, geografisch nah an den Workers-Instanzen und damit nah am Endnutzer.

Was ist Cloudflare D1?

Cloudflare D1 ist Cloudflares serverloser Datenbankdienst, der auf SQLite basiert. Er wurde 2023 in die allgemeine Verfügbarkeit entlassen und ist seither ein fester Bestandteil der Cloudflare-Developer-Plattform. Im Gegensatz zu eigenständigen Datenbankdiensten wie Neon oder Turso ist D1 explizit für den Betrieb innerhalb von Cloudflare Workers konzipiert.

Das Besondere an D1: Die Datenbank wird direkt an einen Worker gebunden – über ein sogenanntes Binding. Der Worker bekommt dabei ein Objekt, über das er SQL-Abfragen ausführen kann, ohne eine externe Verbindung aufzubauen. Aus Entwicklerperspektive ist das ähnlich wie ein lokaler Funktionsaufruf, der jedoch in Wirklichkeit auf einer global verteilten Datenbankinfrastruktur operiert.

SQLite am Edge: Das Konzept

SQLite ist die meistgenutzte Datenbank der Welt – allerdings klassischerweise für lokale, dateibasierte Speicherung. Der Einsatz von SQLite als verteilte Cloud-Datenbank ist zunächst kontraintuitiv, hat aber handfeste Vorteile:

Cloudflare repliziert D1-Datenbanken automatisch in mehrere Regionen. Lesezugriffe werden an das nächstgelegene Replikat geleitet, was Latenzen im einstelligen Millisekunden-Bereich ermöglicht. Schreibvorgänge gehen an die primäre Instanz und werden danach propagiert.

Wrangler-Integration und Projekt-Setup

Das Erstellen und Verwalten von D1-Datenbanken erfolgt über Wrangler, Cloudflares offizielles CLI-Werkzeug. Eine neue Datenbank anlegen ist ein einzelner Befehl:

npx wrangler d1 create meine-datenbank

Wrangler gibt danach die Datenbank-ID aus, die in die wrangler.toml-Konfigurationsdatei eingetragen wird:

[[d1_databases]]
binding = "DB"
database_name = "meine-datenbank"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Damit steht das Binding im Worker unter env.DB zur Verfügung. Lokale Entwicklung funktioniert ebenfalls über Wrangler – wrangler dev simuliert D1 vollständig lokal mit einer echten SQLite-Datei.

Workers Binding: Datenbankzugriff im Worker

Das Workers Binding ist der eleganteste Aspekt von D1. Im Worker-Code sieht ein Datenbankzugriff so aus:

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const userId = url.searchParams.get('id');

    const { results } = await env.DB.prepare(
      'SELECT id, name, email FROM users WHERE id = ?'
    ).bind(userId).all();

    return Response.json(results);
  },
};

Die API ist bewusst einfach gehalten: .prepare() erstellt ein vorbereitetes Statement, .bind() setzt Parameter (mit automatischer SQL-Injection-Prävention) und .all(), .first() oder .run() führen die Abfrage aus. Für Transaktionen steht env.DB.batch([...]) zur Verfügung, das mehrere Statements atomar ausführt.

Tipp: D1 lässt sich hervorragend mit Drizzle ORM kombinieren. Drizzle bietet einen nativen D1-Adapter, der das env.DB-Objekt entgegennimmt und darüber alle gewohnten, typgesicherten Abfragen ermöglicht. Das Setup reduziert Boilerplate erheblich und macht den Code leichter testbar.

REST-API für externe Zugriffe

Neben dem Workers-Binding bietet D1 auch eine HTTP-basierte REST-API, über die Abfragen von außerhalb des Cloudflare-Netzwerks ausgeführt werden können – beispielsweise aus CI/CD-Pipelines, lokalen Verwaltungstools oder anderen Cloud-Funktionen. Die API erfordert einen Cloudflare-API-Token mit D1-Berechtigungen und ist unter dem Endpoint https://api.cloudflare.com/client/v4/accounts/{account_id}/d1/database/{database_id}/query erreichbar.

Für reguläre Anwendungslogik ist das Workers-Binding jedoch der bevorzugte Weg – es ist schneller, sicherer und erfordert keine API-Keys im Anwendungscode.

Preismodell und Free-Tier

D1 ist im Cloudflare Workers Free-Plan enthalten und bietet großzügige Limits ohne Kreditkarte: 5 GB Gesamtspeicher und 25 Millionen gelesene Zeilen pro Tag. Das deckt die allermeisten kleineren Anwendungen vollständig ab.

Im Workers Paid-Plan (5 $/Monat) steigt das Limit auf 50 GB Speicher und 50 Milliarden gelesene Zeilen pro Monat. Schreibvorgänge kosten 1 $ pro Milliarde geschriebener Zeilen. Dieses Preismodell ist für Lese-intensive Anwendungen extrem günstig.

Schema-Migrationen mit D1

D1 unterstützt SQL-Migrationsdateien, die über Wrangler eingespielt werden. Der empfohlene Workflow sieht eine migrations/-Verzeichnisstruktur mit nummerierten SQL-Dateien vor:

# Migration ausführen (lokal)
npx wrangler d1 migrations apply meine-datenbank --local

# Migration auf Produktion einspielen
npx wrangler d1 migrations apply meine-datenbank

Wrangler trackt dabei, welche Migrationen bereits angewendet wurden, und führt nur neue Dateien aus. Für Projekte mit Drizzle ORM generiert drizzle-kit kompatible SQL-Dateien, die direkt als D1-Migrationen verwendet werden können.

Achtung: Cloudflare D1 unterstützt derzeit keine ALTER TABLE ADD COLUMN-Statements für Spalten mit NOT-NULL-Constraint ohne Default-Wert – eine bekannte SQLite-Einschränkung. Außerdem fehlen einige PostgreSQL-spezifische Features wie Window-Funktionen in älteren SQLite-Versionen. Wer komplexe relationale Abfragen oder Postgres-Extensions benötigt, ist mit Neon besser bedient.

Praktisches Beispiel: Worker mit D1

Ein vollständiges Beispiel für eine einfache REST-API mit D1 zeigt das Zusammenspiel aller Komponenten:

// src/index.ts
import { Hono } from 'hono';

type Bindings = { DB: D1Database };

const app = new Hono<{ Bindings: Bindings }>();

app.get('/posts', async (c) => {
  const { results } = await c.env.DB
    .prepare('SELECT id, title, created_at FROM posts ORDER BY created_at DESC LIMIT 20')
    .all();
  return c.json(results);
});

app.post('/posts', async (c) => {
  const { title, content } = await c.req.json();
  await c.env.DB
    .prepare('INSERT INTO posts (title, content) VALUES (?, ?)')
    .bind(title, content)
    .run();
  return c.json({ success: true }, 201);
});

export default app;

Mit Hono als schlankem Web-Framework, D1 als Datenbank und Cloudflare Workers als Runtime entsteht eine vollständige API, die global in unter 50 Millisekunden antwortet – und das bei minimalem operativem Aufwand.

Vergleich: D1 vs. Turso vs. PlanetScale vs. Neon

Kriterium Cloudflare D1 Turso PlanetScale Neon
Edge-nativ Ja (CF-Netzwerk) Ja (30+ Regionen) Teilweise Nein (serverless)
SQL-Dialekt SQLite SQLite (libSQL) MySQL PostgreSQL
Workers-Integration Native Binding Via HTTP/libSQL Via HTTP Via HTTP-Driver
Free-Tier Speicher 5 GB 9 GB, 500 DBs 0,5 GB
Free-Tier Reads/Tag 25 Mio. Zeilen 1 Mrd. Zeilen/Monat 191,9 Compute-Std./Monat
DB-Branching Nein Nein Ja Ja
Embedded Replicas Nein Ja Nein Nein
Multi-Tenant (DB/User) Eingeschränkt Ausgezeichnet Mittel Gut

Fazit

Cloudflare D1 ist die natürlichste Datenbankwahl, wenn die Anwendung ohnehin auf Cloudflare Workers läuft. Das native Binding eliminiert Verbindungskomplexität, das großzügige Free-Tier senkt die Einstiegshürde auf null, und die tiefe Wrangler-Integration macht Entwicklung und Deployment reibungslos. Für Anwendungen außerhalb des Cloudflare-Ökosystems oder mit komplexen PostgreSQL-Anforderungen sind Neon oder Turso die bessere Wahl – aber wer den Cloudflare-Stack schätzt, wird D1 kaum mehr missen wollen.

Häufige Fragen zu Cloudflare D1

Kann ich D1 auch außerhalb von Cloudflare Workers verwenden?
Ja, über die REST-API lässt sich D1 von beliebigen Clients aus abfragen. Das Binding ist jedoch exklusiv für Workers und Pages Functions. Für Nicht-Cloudflare-Umgebungen ist Turso (das ebenfalls libSQL/SQLite nutzt) oft die bessere Alternative.
Wie verhaltet sich D1 bei sehr hohem Schreibaufkommen?
D1 ist optimiert für Lese-intensive Workloads. Bei sehr hohem, gleichzeitigem Schreibaufkommen können Locks auftreten, da SQLite intern seriell schreibt. Für Write-Heavy-Anwendungen (z.B. Logging, Metriken) empfiehlt sich eine Kombination mit Cloudflare Analytics Engine oder einem dedizierten Write-Store.
Werden Daten in D1 georedundant gespeichert?
Ja, Cloudflare repliziert D1-Daten automatisch über mehrere Rechenzentren. Lesezugriffe werden geografisch optimiert, und die Primärinstanz wird bei Ausfall automatisch ersetzt. Details zur genauen Replikationsstrategie finden sich in den D1-Limits und SLA-Dokumentation.
Wie funktioniert lokale Entwicklung mit D1?
Wrangler simuliert D1 lokal mit einer SQLite-Datei im .wrangler/state/-Verzeichnis. Mit wrangler dev stehen alle D1-APIs offline verfügbar. Migrationen können mit dem Flag --local getrennt für lokale und Produktionsdatenbanken eingespielt werden.