Convex-better-auth-dual-database
/SKILLComprendre l'architecture à double base de données de Convex + Better Auth. À utiliser lorsque : (1) « Utilisateur introuvable »
name: convex-better-auth-dual-database
description: |
Understanding Convex + Better Auth dual-database architecture. Use when: (1) "User not found"
errors during login/password reset but user appears to exist, (2) users exist in app's users
table but can't authenticate, (3) need to create admin users in production, (4) debugging
auth flows in Convex + Better Auth setup. Better Auth stores users in component tables
separate from app tables.
author: Claude Code
version: 1.0.0
date: 2026-01-21
Convex + Better Auth Dual-Database Architecture
Problem
When using Better Auth with Convex, users exist in TWO separate locations:
- Better Auth component tables:
betterAuth.user,betterAuth.account,betterAuth.session - App's users table: Your custom
userstable in the main schema
This causes confusing errors like "User not found" when the user exists in one location but not the other.
Context / Trigger Conditions
- "User not found" during login or password reset, but you see the user in your app's database
- User can't authenticate despite having a record in the
userstable - Need to manually create an admin user in production
- Debugging auth flows where users should exist but operations fail
- Export shows users in
_components/betterAuth/user/documents.jsonlseparate fromusers/documents.jsonl
Architecture
┌─────────────────────────────────────────┐
│ Better Auth Component │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ user table │ │ account table │ │
│ │ - email │ │ - password hash │ │
│ │ - name │ │ - providerId │ │
│ │ - verified │ │ - userId │ │
│ └─────────────┘ └──────────────────┘ │
└─────────────────────────────────────────┘
│
│ Login/Auth happens here
▼
┌─────────────────────────────────────────┐
│ App's users table │
│ - email │
│ - name │
│ - isAdmin │
│ - subscriptionStatus │
│ - (business-specific fields) │
└─────────────────────────────────────────┘
Synced via syncFromAuth mutationKey Points
- Authentication uses Better Auth tables ONLY
- Login validates against
betterAuth.userandbetterAuth.account - Password hashes are stored in
betterAuth.account - Sessions are in
betterAuth.session
- App's users table is for business logic
- Stores app-specific fields (isAdmin, subscriptionStatus, etc.)
- Must be synced AFTER Better Auth user is created
- Sync happens via a mutation like
syncFromAuth
- Component tables can't be directly accessed
- Can't write mutations that query
betterAuth.user - Can't import directly to component tables via
convex import --table - Must use export/modify/import workflow for manual changes
Solution: Creating Users Manually
Step 1: Create Better Auth user (via API)
curl -X POST "https://yourapp.com/api/auth/sign-up/email" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "your-password",
"name": "Admin User"
}'Step 2: Create app user (via Convex mutation)
// convex/adminSetup.ts
export const createAdminUser = mutation({
args: { email: v.string(), name: v.string(), setupSecret: v.string() },
handler: async (ctx, args) => {
// Verify secret
if (args.setupSecret !== process.env.ADMIN_SECRET) {
throw new Error("Unauthorized");
}
const userId = await ctx.db.insert("users", {
email: args.email,
name: args.name,
isAdmin: true,
subscriptionStatus: "ACTIVE",
});
return { userId };
},
});Step 3: Run the mutation
npx convex run --prod adminSetup:createAdminUser \
'{"email": "admin@example.com", "name": "Admin", "setupSecret": "your-secret"}'Solution: Deleting/Modifying Better Auth Users
Since you can't access component tables directly:
- Export production data
npx convex export --prod --path /tmp/convex-export- Extract and modify
unzip /tmp/convex-export -d /tmp/convex-data
# Edit _components/betterAuth/user/documents.jsonl
# Edit _components/betterAuth/account/documents.jsonl- Reimport
# Recreate zip with modifications
cd /tmp/convex-data && zip -r ../convex-modified.zip .
npx convex import --prod --replace-all -y /tmp/convex-modified.zipVerification
- Check both tables when debugging:
users/documents.jsonlAND_components/betterAuth/user/documents.jsonl - User must exist in BOTH locations for full functionality
- Better Auth user allows authentication
- App user allows business logic (admin access, subscriptions, etc.)
Notes
- The
syncFromAuthpattern is common: on first login, copy Better Auth