Skip to content

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: user
CREATE 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: session
CREATE 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 Auth
BETTER_AUTH_SECRET=<random-32-char-secret>
BETTER_AUTH_URL=http://localhost:4321
# Cloudflare
CLOUDFLARE_ACCOUNT_ID=<your-account-id>
AUTH_DB_ID=<your-d1-database-id>
AUTH_KV_ID=4a990eb34db9430284b0961ac5d107dd
# Environment
ENVIRONMENT=dev

4. 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

scripts/create-cloudflare-resources.sh
#!/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
;;
esac
done
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 1
fi
echo "=== Création des ressources Cloudflare pour Better Auth (environnement: $ENV) ==="
echo ""
# Création D1 Database
echo "📊 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 1
fi
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

Terminal window
# 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=prod

4.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é :

scripts/migrate-d1.sh

Génération du schéma :

Terminal window
./scripts/migrate-d1.sh --env=dev --action=generate

4.4. Migrations D1 via Script2/3

Application des migrations :

Terminal window
# Environnement dev
./scripts/migrate-d1.sh --env=dev --action=apply
# Environnement prod
./scripts/migrate-d1.sh --env=prod --action=apply

4.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

Terminal window
cd apps/web
pnpm add better-auth better-auth-cloudflare drizzle-orm
pnpm add -D @better-auth/drizzle-adapter drizzle-kit

5.2. Configuration Better Auth1/2

5.2. Configuration Better Auth2/2

apps/web/src/auth/index.ts
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

apps/web/src/db/auth.schema.ts
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

apps/web/src/env.d.ts
interface Env {
AUTH_DB: D1Database;
AUTH_KV: KVNamespace;
ENVIRONMENT: 'dev' | 'prod';
}

6.2. Middleware Utilisant Better Auth

apps/web/src/middleware.ts
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é

  1. Secret Generation : Utiliser crypto.randomUUID() ou outil de génération de secrets
  2. Environment Variables : Jamais commitées, utiliser .env + .env.example
  3. D1 Backups : Configurer backups automatiques Cloudflare
  4. KV TTL : Sessions en KV avec TTL approprié
  5. Encryption : Better Auth gère le hash des passwords automatiquement

9. Migration depuis Auth-Handler (CODE)

9.1. Script Migration Sessions

scripts/migrate-sessions.ts
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

Terminal window
# 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écessaire

10. 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

Terminal window
# Créer D1 database
wrangler d1 create amn-auth-db
# Mettre à jour wrangler.toml avec l'ID

11.2. Migrations

Terminal window
# Générer schéma
npx @better-auth-cloudflare/cli migrate --generate
# Appliquer migrations
npx @better-auth-cloudflare/cli migrate --apply dev

11.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)
Switch to normal mode

Contents