Configuration D1 + KV pour Better Auth Cloudflare
Configuration D1 + KV pour Better Auth Cloudflare
Documentation technique
Objet : Spécification configuration Cloudflare D1 et KV pour Better Auth
Type : Documentation Technique (DAT)
Couche : Couche 03 (Backend - Cloudflare)
Emplacement :apps/web/wrangler.toml+ configuration Better Auth
Statut : DOC (Proposé)
1. Vue d’ensemble (DOC)
1.1. Description
Configuration détaillée des ressources Cloudflare D1 et KV nécessaires pour Better Auth Cloudflare, incluant la création des bases de données, les bindings wrangler.jsonc (conformément aux recommandations Cloudflare), l’observabilité activée, et la configuration de Better Auth.
1.2. Objectif
- Définir l’architecture de stockage D1 + KV
- Spécifier la configuration wrangler.jsonc avec observabilité
- Documenter la création des ressources Cloudflare via script bash
- Configurer Better Auth avec D1 et KV
2. Architecture de Stockage (CODE)
2.1. Ressources Cloudflare1/2
D1 Database : amn-auth-db
- Stockage principal des utilisateurs et sessions
- Schéma géré par Drizzle ORM
- Migrations automatiques via CLI Better Auth
- Source de vérité pour les données d’authentification
KV Namespace : AUTH_SESSIONS_KV (existant)
2.1. Ressources Cloudflare2/2
- Cache des sessions : Pour éviter de requêter D1 à chaque vérification de session (ce qui consommerait votre quota d’écriture/lecture D1)
- Amélioration des performances : Lecture KV plus rapide que D1 (edge cache)
- Optimisation des coûts : Réduction des requêtes D1 (quota limité)
- Fallback : Disponibilité en cas de panne D1
2.2. Flux de Vérification de Session avec KV1/2
Requête de vérification session │ ▼ [Lire KV] │ ┌────┴────┐ │ │ ▼ ▼[Hit KV] [Miss KV] │ │ │ ▼ │ [Lire D1] │ │ │ ▼ │ [Écrire KV] │ │ └────┬────┘ │ ▼ [Retour session]2.2. Flux de Vérification de Session avec KV2/2
Avantages de ce flux :
- Première requête : Lecture D1 + écriture KV (coût D1)
- Requêtes suivantes : Lecture KV uniquement (coût réduit)
- Performance : KV est edge-cached, plus rapide que D1
- Coût : KV lectures sont moins chères que D1 lectures
- Scalabilité : KV gère mieux les pics de traffic
2.2. Schéma D1 (Géré par Better Auth)1/2
Better Auth génère automatiquement le schéma suivant via Drizzle :
2.2. Schéma D1 (Géré par Better Auth)2/2
-- Table: userCREATE TABLE user ( id TEXT PRIMARY KEY, email TEXT UNIQUE NOT NULL, emailVerified INTEGER DEFAULT 0, name TEXT, image TEXT, createdAt INTEGER NOT NULL, updatedAt INTEGER NOT NULL);
-- Table: sessionCREATE TABLE session ( id TEXT PRIMARY KEY, userId TEXT NOT NULL, expiresAt INTEGER NOT NULL, token TEXT UNIQUE NOT NULL, ipAddress TEXT, userAgent TEXT, createdAt INTEGER NOT NULL, updatedAt INTEGER NOT NULL, FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE);
-- Table: account (pour OAuth providers)CREATE TABLE account ( id TEXT PRIMARY KEY, userId TEXT NOT NULL, accountId TEXT NOT NULL, providerId TEXT NOT NULL, accessToken TEXT, refreshToken TEXT, idToken TEXT, expiresAt INTEGER, password TEXT, createdAt INTEGER NOT NULL, updatedAt INTEGER NOT NULL, FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE);
-- Table: verification (pour email verification)CREATE TABLE verification ( id TEXT PRIMARY KEY, identifier TEXT NOT NULL, value TEXT NOT NULL, expiresAt INTEGER NOT NULL, createdAt INTEGER NOT NULL, updatedAt INTEGER NOT NULL);3. Configuration Wrangler (CODE)
3.1. apps/web/wrangler.jsonc1/2
Conformément aux recommandations Cloudflare, nous utilisons wrangler.jsonc au lieu de wrangler.toml avec observabilité activée.
3.1. apps/web/wrangler.jsonc2/2
{ "$schema": "node_modules/wrangler/config-schema.json", "name": "amn-dev", "compatibility_date": "2026-09-01", "compatibility_flags": ["nodejs_compat"],
"observability": { "enabled": true, "logs": { "head_sampling_rate": 1 }, "traces": { "enabled": true, "head_sampling_rate": 0.01 } },
"d1_databases": [ { "binding": "AUTH_DB", "database_name": "amn-auth-db", "database_id": "<your-d1-database-id>", "migrations_dir": "drizzle" } ],
"kv_namespaces": [ { "binding": "AUTH_KV", "id": "4a990eb34db9430284b0961ac5d107dd" } ],
"services": [ { "binding": "FORM_HANDLER", "service": "amn-form-handler-dev" } ],
"env": { "dev": { "name": "amn-dev", "observability": { "enabled": true, "logs": { "head_sampling_rate": 1 }, "traces": { "enabled": true, "head_sampling_rate": 0.01 } }, "d1_databases": [ { "binding": "AUTH_DB", "database_name": "amn-auth-db", "database_id": "<your-d1-database-id>", "migrations_dir": "drizzle" } ], "kv_namespaces": [ { "binding": "AUTH_KV", "id": "4a990eb34db9430284b0961ac5d107dd" } ], "services": [ { "binding": "FORM_HANDLER", "service": "amn-form-handler-dev" } ] }, "prod": { "name": "amn-prod", "observability": { "enabled": true, "logs": { "head_sampling_rate": 1 }, "traces": { "enabled": true, "head_sampling_rate": 0.01 } }, "d1_databases": [ { "binding": "AUTH_DB", "database_name": "amn-auth-db-prod", "database_id": "<your-d1-database-id-prod>", "migrations_dir": "drizzle" } ], "kv_namespaces": [ { "binding": "AUTH_KV", "id": "<your-kv-namespace-id-prod>" } ], "services": [ { "binding": "FORM_HANDLER", "service": "amn-form-handler-prod" } ] } }}3.2. Environment Variables (.env)
# Better AuthBETTER_AUTH_SECRET=<random-32-char-secret>BETTER_AUTH_URL=http://localhost:4321
# CloudflareCLOUDFLARE_ACCOUNT_ID=<your-account-id>AUTH_DB_ID=<your-d1-database-id>AUTH_KV_ID=4a990eb34db9430284b0961ac5d107dd
# EnvironmentENVIRONMENT=dev4. Création des Ressources Cloudflare (CODE)
4.1. Script de Création des Ressources1/2
Conformément à l’approche du projet, les ressources Cloudflare sont créées via script bash automatisé :
4.1. Script de Création des Ressources2/2
#!/bin/bash# Création des ressources Cloudflare pour Better Auth (D1 + KV)# Usage: ./scripts/create-cloudflare-resources.sh --env=dev
set -e
ENV="dev"
while [[ $# -gt 0 ]]; do case $1 in --env=*) ENV="${1#*=}" shift ;; *) echo "❌ Argument inconnu: $1" echo "Usage: ./scripts/create-cloudflare-resources.sh --env=dev" exit 1 ;; esacdone
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"PROJECT_ROOT="$ROOT_DIR/.."cd "$PROJECT_ROOT"
if ! npx wrangler whoami >/dev/null 2>&1; then echo "❌ Wrangler n'est pas connecté. Connectez-vous avec: npx wrangler login" exit 1fi
echo "=== Création des ressources Cloudflare pour Better Auth (environnement: $ENV) ==="echo ""
# Création D1 Databaseecho "📊 Création D1 Database 'amn-auth-db-$ENV'..."D1_OUTPUT=$(npx wrangler d1 create "amn-auth-db-$ENV" 2>&1)D1_ID=$(echo "$D1_OUTPUT" | grep -oP 'database_id = "\K[^"]+')
if [ -z "$D1_ID" ]; then echo "⚠️ Erreur lors de la création D1 ou base existe déjà" exit 1fi
echo "✅ D1 Database créée avec ID: $D1_ID"echo ""
# Création KV Namespace (si nécessaire pour prod)if [ "$ENV" = "prod" ]; then echo "🗄️ Création KV Namespace 'amn-auth-kv-$ENV'..." KV_OUTPUT=$(npx wrangler kv namespace create "amn-auth-kv-$ENV" 2>&1) KV_ID=$(echo "$KV_OUTPUT" | grep -oP 'id = "\K[^"]+')
if [ -z "$KV_ID" ]; then echo "⚠️ Erreur lors de la création KV ou namespace existe déjà" exit 1 fi
echo "✅ KV Namespace créé avec ID: $KV_ID"else KV_ID="4a990eb34db9430284b0961ac5d107dd" echo "✅ Utilisation KV existant (dev): $KV_ID"fi
echo ""echo "=== Résumé des ressources créées ==="echo "D1 Database ID: $D1_ID"echo "KV Namespace ID: $KV_ID"echo ""echo "⚠️ IMPORTANT: Mettez à jour wrangler.jsonc avec ces IDs"echo ""4.2. Exécution du Script
# Créer les ressources pour dev./scripts/create-cloudflare-resources.sh --env=dev
# Créer les ressources pour prod./scripts/create-cloudflare-resources.sh --env=prod4.3. Mise à jour wrangler.jsonc
Après l’exécution du script, mettre à jour les IDs dans apps/web/wrangler.jsonc :
"d1_databases": [ { "binding": "AUTH_DB", "database_name": "amn-auth-db", "database_id": "<votre-d1-id-ici>", // Remplacer par l'ID retourné "migrations_dir": "drizzle" }],"kv_namespaces": [ { "binding": "AUTH_KV", "id": "4a990eb34db9430284b0961ac5d107dd" // Existant pour dev }]4.4. Migrations D1 via Script1/3
Conformément à l’approche du projet, les migrations D1 sont effectuées via script bash automatisé :
Génération du schéma :
./scripts/migrate-d1.sh --env=dev --action=generate4.4. Migrations D1 via Script2/3
Application des migrations :
# Environnement dev./scripts/migrate-d1.sh --env=dev --action=apply
# Environnement prod./scripts/migrate-d1.sh --env=prod --action=apply4.4. Migrations D1 via Script3/3
Le script vérifie automatiquement :
- Que wrangler est connecté
- Que Better Auth Cloudflare CLI est installé
- L’environnement cible (dev/prod)
- L’action demandée (generate/apply)
5. Configuration Better Auth (CODE)
5.1. Installation des Dépendances
cd apps/webpnpm add better-auth better-auth-cloudflare drizzle-ormpnpm add -D @better-auth/drizzle-adapter drizzle-kit5.2. Configuration Better Auth1/2
5.2. Configuration Better Auth2/2
import { betterAuth } from "better-auth";import { withCloudflare } from "better-auth-cloudflare";import { drizzleAdapter } from "@better-auth/drizzle-adapter";import { drizzle } from "drizzle-orm/d1";import * as schema from "./db/schema";
export function createAuth(env: any) { const db = drizzle(env.AUTH_DB, { schema });
return betterAuth({ ...withCloudflare( { d1: { db, options: { usePlural: true } }, kv: env.AUTH_KV, autoDetectIpAddress: true }, { database: drizzleAdapter(db, { provider: "sqlite" }), session: { expiresIn: 60 * 60 * 24 * 7, // 7 days updateAge: 60 * 60 * 24 // 1 day }, advanced: { cookiePrefix: "amn-auth", useSecureCookies: env.ENVIRONMENT === 'prod' }, account: { accountLinking: { enabled: false } } } ) });}5.3. Schéma Drizzle (Auto-généré)1/2
Better Auth génère automatiquement le schéma via son CLI. Le fichier généré sera :
5.3. Schéma Drizzle (Auto-généré)2/2
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const user = sqliteTable("user", { id: text("id").primaryKey(), email: text("email").notNull().unique(), emailVerified: integer("emailVerified", { mode: "boolean" }).default(false), name: text("name"), image: text("image"), createdAt: integer("createdAt").notNull(), updatedAt: integer("updatedAt").notNull(),});
export const session = sqliteTable("session", { id: text("id").primaryKey(), userId: text("userId").notNull().references(() => user.id, { onDelete: "cascade" }), expiresAt: integer("expiresAt").notNull(), token: text("token").notNull().unique(), ipAddress: text("ipAddress"), userAgent: text("userAgent"), createdAt: integer("createdAt").notNull(), updatedAt: integer("updatedAt").notNull(),});
export const account = sqliteTable("account", { id: text("id").primaryKey(), userId: text("userId").notNull().references(() => user.id, { onDelete: "cascade" }), accountId: text("accountId").notNull(), providerId: text("providerId").notNull(), accessToken: text("accessToken"), refreshToken: text("refreshToken"), idToken: text("idToken"), expiresAt: integer("expiresAt"), password: text("password"), createdAt: integer("createdAt").notNull(), updatedAt: integer("updatedAt").notNull(),});
export const verification = sqliteTable("verification", { id: text("id").primaryKey(), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: integer("expiresAt").notNull(), createdAt: integer("createdAt").notNull(), updatedAt: integer("updatedAt").notNull(),});6. Intégration avec Middleware Astro (CODE)
6.1. Configuration TypeScript
interface Env { AUTH_DB: D1Database; AUTH_KV: KVNamespace; ENVIRONMENT: 'dev' | 'prod';}6.2. Middleware Utilisant Better Auth
import { defineMiddleware } from 'astro:middleware';import { createAuth } from './auth/index';
export const onRequest = defineMiddleware(async (context, next) => { const { pathname } = context.url;
// Pour les routes de documentation privées if (pathname.startsWith('/docs/') || pathname.startsWith('/gettingStarted/')) { const auth = createAuth(context.locals.runtime.env); const session = await auth.api.getSession({ headers: context.request.headers });
if (!session) { return context.redirect(`/login?redirectTo=${encodeURIComponent(pathname)}`); }
context.locals.user = session.user; }
return next();});7. Contrats d’Entrée/Sortie (SPEC)
7.1. Wrangler Configuration
| Binding | Type | Resource | Description |
|---|---|---|---|
AUTH_DB |
D1 | amn-auth-db |
Base de données users/sessions |
AUTH_KV |
KV | AUTH_SESSIONS_KV |
Secondary storage cache |
7.2. Environment Variables
| Variable | Type | Description | Exemple |
|---|---|---|---|
BETTER_AUTH_SECRET |
string | Secret pour signing cookies | random-32-char-secret |
BETTER_AUTH_URL |
string | URL de l’application | http://localhost:4321 |
AUTH_DB_ID |
string | ID de la base D1 | xxxxxxxx-xxxx-xxxx-xxxx |
AUTH_KV_ID |
string | ID du namespace KV | 4a990eb34db9430284b0961ac5d107dd |
7.3. Better Auth Configuration
| Option | Valeur | Description |
|---|---|---|
session.expiresIn |
604800 (7 days) |
Durée de vie session |
session.updateAge |
86400 (1 day) |
Fréquence rafraîchissement |
cookiePrefix |
amn-auth |
Préfixe cookies |
useSecureCookies |
env === 'prod' |
Cookies HTTPS en prod |
8. Sécurité
- Secret Generation : Utiliser
crypto.randomUUID()ou outil de génération de secrets - Environment Variables : Jamais commitées, utiliser
.env+.env.example - D1 Backups : Configurer backups automatiques Cloudflare
- KV TTL : Sessions en KV avec TTL approprié
- Encryption : Better Auth gère le hash des passwords automatiquement
9. Migration depuis Auth-Handler (CODE)
9.1. Script Migration Sessions
import { createClient } from '@cloudflare/d1';
export async function migrateSessions() { // Lire les sessions depuis KV existant // Créer les utilisateurs et sessions dans D1 // Mettre à jour les cookies}9.2. Rollback Plan
# En cas de problème, restaurer l'ancien système# 1. Restaurer configuration wrangler.toml (sans D1)# 2. Redémarrer auth-handler# 3. Restaurer backup D1 si nécessaire10. Tests Attendus (TEST)
10.1. Tests Infrastructure
- D1 database créée et accessible
- KV namespace accessible
- Migrations appliquées avec succès
- Bindings wrangler corrects
10.2. Tests Better Auth
- Configuration Better Auth valide
- Connexion D1 fonctionnelle
- KV secondary storage actif
- Cookies générés correctement
10.3. Tests Intégration
- Middleware utilise Better Auth
- Sessions créées dans D1
- Cache KV fonctionnel
- Typecheck réussi
11. Documentation Utilisateur
11.1. Création Ressources
# Créer D1 databasewrangler d1 create amn-auth-db
# Mettre à jour wrangler.toml avec l'ID11.2. Migrations
# Générer schémanpx @better-auth-cloudflare/cli migrate --generate
# Appliquer migrationsnpx @better-auth-cloudflare/cli migrate --apply dev11.3. Dépannage
- Erreur binding D1 : Vérifier wrangler.toml et redémarrer dev server
- Migrations échouent : Vérifier connectivity D1 et permissions
- KV non accessible : Vérifier ID KV namespace et binding
12. Historique des Modifications
| Date | Version | Description | Auteur |
|---|---|---|---|
| 2026-09-27 | 1.0 | Création initiale DAT Configuration D1 + KV | Team (Agent) |