578 lines
10 KiB
Markdown
578 lines
10 KiB
Markdown
# Database Integration
|
|
|
|
Better Auth supports multiple databases and ORMs for flexible data persistence.
|
|
|
|
## Supported Databases
|
|
|
|
- SQLite
|
|
- PostgreSQL
|
|
- MySQL/MariaDB
|
|
- MongoDB
|
|
- Any database with adapter support
|
|
|
|
## Direct Database Connection
|
|
|
|
### SQLite
|
|
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import Database from "better-sqlite3";
|
|
|
|
export const auth = betterAuth({
|
|
database: new Database("./sqlite.db"),
|
|
// or
|
|
database: new Database(":memory:") // In-memory for testing
|
|
});
|
|
```
|
|
|
|
### PostgreSQL
|
|
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import { Pool } from "pg";
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
// or explicit config
|
|
host: "localhost",
|
|
port: 5432,
|
|
user: "postgres",
|
|
password: "password",
|
|
database: "myapp"
|
|
});
|
|
|
|
export const auth = betterAuth({
|
|
database: pool
|
|
});
|
|
```
|
|
|
|
### MySQL
|
|
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import { createPool } from "mysql2/promise";
|
|
|
|
const pool = createPool({
|
|
host: "localhost",
|
|
user: "root",
|
|
password: "password",
|
|
database: "myapp",
|
|
waitForConnections: true,
|
|
connectionLimit: 10
|
|
});
|
|
|
|
export const auth = betterAuth({
|
|
database: pool
|
|
});
|
|
```
|
|
|
|
## ORM Adapters
|
|
|
|
### Drizzle ORM
|
|
|
|
**Install:**
|
|
```bash
|
|
npm install drizzle-orm better-auth
|
|
```
|
|
|
|
**Setup:**
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
import { Pool } from "pg";
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL
|
|
});
|
|
|
|
const db = drizzle(pool);
|
|
|
|
export const auth = betterAuth({
|
|
database: drizzleAdapter(db, {
|
|
provider: "pg", // "pg" | "mysql" | "sqlite"
|
|
schema: {
|
|
// Optional: custom table names
|
|
user: "users",
|
|
session: "sessions",
|
|
account: "accounts",
|
|
verification: "verifications"
|
|
}
|
|
})
|
|
});
|
|
```
|
|
|
|
**Generate Schema:**
|
|
```bash
|
|
npx @better-auth/cli generate --adapter drizzle
|
|
```
|
|
|
|
### Prisma
|
|
|
|
**Install:**
|
|
```bash
|
|
npm install @prisma/client better-auth
|
|
```
|
|
|
|
**Setup:**
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
import { PrismaClient } from "@prisma/client";
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
export const auth = betterAuth({
|
|
database: prismaAdapter(prisma, {
|
|
provider: "postgresql", // "postgresql" | "mysql" | "sqlite"
|
|
})
|
|
});
|
|
```
|
|
|
|
**Generate Schema:**
|
|
```bash
|
|
npx @better-auth/cli generate --adapter prisma
|
|
```
|
|
|
|
**Apply to Prisma:**
|
|
```bash
|
|
# Add generated schema to schema.prisma
|
|
npx prisma migrate dev --name init
|
|
npx prisma generate
|
|
```
|
|
|
|
### Kysely
|
|
|
|
**Install:**
|
|
```bash
|
|
npm install kysely better-auth
|
|
```
|
|
|
|
**Setup:**
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import { kyselyAdapter } from "better-auth/adapters/kysely";
|
|
import { Kysely, PostgresDialect } from "kysely";
|
|
import { Pool } from "pg";
|
|
|
|
const db = new Kysely({
|
|
dialect: new PostgresDialect({
|
|
pool: new Pool({
|
|
connectionString: process.env.DATABASE_URL
|
|
})
|
|
})
|
|
});
|
|
|
|
export const auth = betterAuth({
|
|
database: kyselyAdapter(db, {
|
|
provider: "pg"
|
|
})
|
|
});
|
|
```
|
|
|
|
**Auto-migrate with Kysely:**
|
|
```bash
|
|
npx @better-auth/cli migrate --adapter kysely
|
|
```
|
|
|
|
### MongoDB
|
|
|
|
**Install:**
|
|
```bash
|
|
npm install mongodb better-auth
|
|
```
|
|
|
|
**Setup:**
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
import { MongoClient } from "mongodb";
|
|
|
|
const client = new MongoClient(process.env.MONGODB_URI!);
|
|
await client.connect();
|
|
|
|
export const auth = betterAuth({
|
|
database: mongodbAdapter(client, {
|
|
databaseName: "myapp"
|
|
})
|
|
});
|
|
```
|
|
|
|
**Generate Collections:**
|
|
```bash
|
|
npx @better-auth/cli generate --adapter mongodb
|
|
```
|
|
|
|
## Core Database Schema
|
|
|
|
Better Auth requires these core tables/collections:
|
|
|
|
### User Table
|
|
|
|
```sql
|
|
CREATE TABLE user (
|
|
id TEXT PRIMARY KEY,
|
|
email TEXT UNIQUE NOT NULL,
|
|
emailVerified BOOLEAN DEFAULT FALSE,
|
|
name TEXT,
|
|
image TEXT,
|
|
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
### Session Table
|
|
|
|
```sql
|
|
CREATE TABLE session (
|
|
id TEXT PRIMARY KEY,
|
|
userId TEXT NOT NULL,
|
|
expiresAt TIMESTAMP NOT NULL,
|
|
ipAddress TEXT,
|
|
userAgent TEXT,
|
|
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE
|
|
);
|
|
```
|
|
|
|
### Account Table
|
|
|
|
```sql
|
|
CREATE TABLE account (
|
|
id TEXT PRIMARY KEY,
|
|
userId TEXT NOT NULL,
|
|
accountId TEXT NOT NULL,
|
|
providerId TEXT NOT NULL,
|
|
accessToken TEXT,
|
|
refreshToken TEXT,
|
|
expiresAt TIMESTAMP,
|
|
scope TEXT,
|
|
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE,
|
|
UNIQUE(providerId, accountId)
|
|
);
|
|
```
|
|
|
|
### Verification Table
|
|
|
|
```sql
|
|
CREATE TABLE verification (
|
|
id TEXT PRIMARY KEY,
|
|
identifier TEXT NOT NULL,
|
|
value TEXT NOT NULL,
|
|
expiresAt TIMESTAMP NOT NULL,
|
|
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
## Schema Generation
|
|
|
|
### Using CLI
|
|
|
|
```bash
|
|
# Generate schema files
|
|
npx @better-auth/cli generate
|
|
|
|
# Specify adapter
|
|
npx @better-auth/cli generate --adapter drizzle
|
|
npx @better-auth/cli generate --adapter prisma
|
|
|
|
# Specify output
|
|
npx @better-auth/cli generate --output ./db/schema.ts
|
|
```
|
|
|
|
### Auto-migrate (Kysely only)
|
|
|
|
```bash
|
|
npx @better-auth/cli migrate
|
|
```
|
|
|
|
For other ORMs, apply generated schema manually.
|
|
|
|
## Custom Fields
|
|
|
|
Add custom fields to user table:
|
|
|
|
```ts
|
|
export const auth = betterAuth({
|
|
user: {
|
|
additionalFields: {
|
|
role: {
|
|
type: "string",
|
|
required: false,
|
|
defaultValue: "user"
|
|
},
|
|
phoneNumber: {
|
|
type: "string",
|
|
required: false
|
|
},
|
|
subscriptionTier: {
|
|
type: "string",
|
|
required: false
|
|
}
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
After adding fields:
|
|
```bash
|
|
npx @better-auth/cli generate
|
|
```
|
|
|
|
Update user with custom fields:
|
|
```ts
|
|
await authClient.updateUser({
|
|
role: "admin",
|
|
phoneNumber: "+1234567890"
|
|
});
|
|
```
|
|
|
|
## Plugin Schema Extensions
|
|
|
|
Plugins add their own tables/fields. Regenerate schema after adding plugins:
|
|
|
|
```bash
|
|
npx @better-auth/cli generate
|
|
```
|
|
|
|
### Two-Factor Plugin Tables
|
|
|
|
- `twoFactor`: Stores TOTP secrets, backup codes
|
|
|
|
### Passkey Plugin Tables
|
|
|
|
- `passkey`: Stores WebAuthn credentials
|
|
|
|
### Organization Plugin Tables
|
|
|
|
- `organization`: Organization data
|
|
- `member`: Organization members
|
|
- `invitation`: Pending invitations
|
|
|
|
## Migration Strategies
|
|
|
|
### Development
|
|
|
|
```bash
|
|
# Generate schema
|
|
npx @better-auth/cli generate
|
|
|
|
# Apply migrations (Kysely)
|
|
npx @better-auth/cli migrate
|
|
|
|
# Or manual (Prisma)
|
|
npx prisma migrate dev
|
|
|
|
# Or manual (Drizzle)
|
|
npx drizzle-kit push
|
|
```
|
|
|
|
### Production
|
|
|
|
```bash
|
|
# Review generated migration
|
|
npx @better-auth/cli generate
|
|
|
|
# Test in staging
|
|
# Apply to production with your ORM's migration tool
|
|
|
|
# Prisma
|
|
npx prisma migrate deploy
|
|
|
|
# Drizzle
|
|
npx drizzle-kit push
|
|
|
|
# Kysely
|
|
npx @better-auth/cli migrate
|
|
```
|
|
|
|
## Connection Pooling
|
|
|
|
### PostgreSQL
|
|
|
|
```ts
|
|
import { Pool } from "pg";
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
max: 20, // Max connections
|
|
idleTimeoutMillis: 30000,
|
|
connectionTimeoutMillis: 2000,
|
|
});
|
|
```
|
|
|
|
### MySQL
|
|
|
|
```ts
|
|
import { createPool } from "mysql2/promise";
|
|
|
|
const pool = createPool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
waitForConnections: true,
|
|
connectionLimit: 10,
|
|
queueLimit: 0
|
|
});
|
|
```
|
|
|
|
## Database URLs
|
|
|
|
### PostgreSQL
|
|
|
|
```env
|
|
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
|
|
# Or with connection params
|
|
DATABASE_URL=postgresql://user:password@localhost:5432/dbname?schema=public&connection_limit=10
|
|
```
|
|
|
|
### MySQL
|
|
|
|
```env
|
|
DATABASE_URL=mysql://user:password@localhost:3306/dbname
|
|
```
|
|
|
|
### SQLite
|
|
|
|
```env
|
|
DATABASE_URL=file:./dev.db
|
|
# Or in-memory
|
|
DATABASE_URL=:memory:
|
|
```
|
|
|
|
### MongoDB
|
|
|
|
```env
|
|
MONGODB_URI=mongodb://localhost:27017/dbname
|
|
# Or Atlas
|
|
MONGODB_URI=mongodb+srv://user:password@cluster.mongodb.net/dbname
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### Indexes
|
|
|
|
Better Auth CLI auto-generates essential indexes:
|
|
- `user.email` (unique)
|
|
- `session.userId`
|
|
- `account.userId`
|
|
- `account.providerId, accountId` (unique)
|
|
|
|
Add custom indexes for performance:
|
|
```sql
|
|
CREATE INDEX idx_session_expires ON session(expiresAt);
|
|
CREATE INDEX idx_user_created ON user(createdAt);
|
|
```
|
|
|
|
### Query Optimization
|
|
|
|
```ts
|
|
// Use connection pooling
|
|
// Enable query caching where applicable
|
|
// Monitor slow queries
|
|
|
|
export const auth = betterAuth({
|
|
advanced: {
|
|
defaultCookieAttributes: {
|
|
sameSite: "lax",
|
|
secure: true,
|
|
httpOnly: true
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
## Backup Strategies
|
|
|
|
### PostgreSQL
|
|
|
|
```bash
|
|
# Backup
|
|
pg_dump dbname > backup.sql
|
|
|
|
# Restore
|
|
psql dbname < backup.sql
|
|
```
|
|
|
|
### MySQL
|
|
|
|
```bash
|
|
# Backup
|
|
mysqldump -u root -p dbname > backup.sql
|
|
|
|
# Restore
|
|
mysql -u root -p dbname < backup.sql
|
|
```
|
|
|
|
### SQLite
|
|
|
|
```bash
|
|
# Copy file
|
|
cp dev.db dev.db.backup
|
|
|
|
# Or use backup command
|
|
sqlite3 dev.db ".backup backup.db"
|
|
```
|
|
|
|
### MongoDB
|
|
|
|
```bash
|
|
# Backup
|
|
mongodump --db=dbname --out=./backup
|
|
|
|
# Restore
|
|
mongorestore --db=dbname ./backup/dbname
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Environment Variables**: Store credentials in env vars, never commit
|
|
2. **Connection Pooling**: Use pools for PostgreSQL/MySQL in production
|
|
3. **Migrations**: Use ORM migration tools, not raw SQL in production
|
|
4. **Indexes**: Add indexes for frequently queried fields
|
|
5. **Backups**: Automate daily backups in production
|
|
6. **SSL**: Use SSL/TLS for database connections in production
|
|
7. **Schema Sync**: Keep schema in sync across environments
|
|
8. **Testing**: Use separate database for tests (in-memory SQLite ideal)
|
|
9. **Monitoring**: Monitor query performance and connection pool usage
|
|
10. **Cleanup**: Periodically clean expired sessions/verifications
|
|
|
|
## Troubleshooting
|
|
|
|
### Connection Errors
|
|
|
|
```ts
|
|
// Add connection timeout
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
connectionTimeoutMillis: 5000
|
|
});
|
|
```
|
|
|
|
### Schema Mismatch
|
|
|
|
```bash
|
|
# Regenerate schema
|
|
npx @better-auth/cli generate
|
|
|
|
# Apply migrations
|
|
# For Prisma: npx prisma migrate dev
|
|
# For Drizzle: npx drizzle-kit push
|
|
```
|
|
|
|
### Migration Failures
|
|
|
|
- Check database credentials
|
|
- Verify database server is running
|
|
- Check for schema conflicts
|
|
- Review migration SQL manually
|
|
|
|
### Performance Issues
|
|
|
|
- Add indexes on foreign keys
|
|
- Enable connection pooling
|
|
- Monitor slow queries
|
|
- Consider read replicas for heavy read workloads
|