Better Auth can also run without any database. See Stateless Session Management for details.
Adapters
Pass a supported database instance to thedatabase option in your auth config. Better Auth has built-in support for SQLite, PostgreSQL, MySQL, and MSSQL via a Kysely adapter, plus first-class ORM adapters for Drizzle and Prisma.
Better Auth supports SQLite, PostgreSQL, MySQL, MSSQL, and more through the built-in Kysely adapter.
CLI
Better Auth ships a CLI tool to manage migrations and generate schema files.Running migrations
The CLI checks your database and prompts you to add missing tables or update existing columns. This is only supported for the built-in Kysely adapter.For PostgreSQL users: the migrate command supports non-default schemas. It automatically detects your
search_path configuration and creates tables in the correct schema.Generating schema
For ORM adapters like Prisma or Drizzle, use thegenerate command to produce the correct schema definition for your ORM. For the built-in Kysely adapter, this generates an SQL file you can run directly.
Programmatic migrations
In environments where the CLI is unavailable (such as Cloudflare Workers), run migrations programmatically:Example: Cloudflare D1
Example: Cloudflare D1
Cloudflare D1 can only be queried through a Cloudflare Worker, so the CLI cannot access it directly. Run migrations through a protected endpoint instead:
auth.ts
src/index.ts
Secondary storage
Secondary storage lets you offload session data, verification records, and rate-limiting counters to a high-performance key-value store like Redis.Interface
Implement theSecondaryStorage interface:
betterAuth:
auth.ts
Official Redis package
Better Auth provides an official Redis storage package backed by ioredis:auth.ts
Manual Redis implementation
You can also implement secondary storage manually using theredis package:
auth.ts
Core schema
Better Auth requires four tables in your database. The types below use TypeScript notation — use the corresponding types for your database.User table
| Field | Type | Description |
|---|---|---|
id | string (PK) | Unique identifier |
name | string | Display name |
email | string (unique) | Email address |
emailVerified | boolean | Whether the email is verified |
image | string (optional) | Avatar URL |
createdAt | Date | Account creation timestamp |
updatedAt | Date | Last update timestamp |
Session table
| Field | Type | Description |
|---|---|---|
id | string (PK) | Unique identifier |
userId | string (FK) | Reference to the user |
token | string (unique) | Session token |
expiresAt | Date | Expiry timestamp |
ipAddress | string (optional) | Client IP address |
userAgent | string (optional) | Client user agent |
createdAt | Date | Creation timestamp |
updatedAt | Date | Last update timestamp |
Account table
| Field | Type | Description |
|---|---|---|
id | string (PK) | Unique identifier |
userId | string (FK) | Reference to the user |
accountId | string | Provider-assigned account ID |
providerId | string | Authentication provider ID |
accessToken | string (optional) | OAuth access token |
refreshToken | string (optional) | OAuth refresh token |
accessTokenExpiresAt | Date (optional) | Access token expiry |
refreshTokenExpiresAt | Date (optional) | Refresh token expiry |
scope | string (optional) | Granted OAuth scopes |
idToken | string (optional) | OIDC ID token |
password | string (optional) | Hashed password (email/password auth) |
createdAt | Date | Creation timestamp |
updatedAt | Date | Last update timestamp |
Verification table
| Field | Type | Description |
|---|---|---|
id | string (PK) | Unique identifier |
identifier | string | Verification request identifier |
value | string | Value to verify |
expiresAt | Date | Expiry timestamp |
createdAt | Date | Creation timestamp |
updatedAt | Date | Last update timestamp |
Custom tables
Custom table and column names
Override table and column names using themodelName and fields properties:
auth.ts
Type inference in your code always uses the original field names (e.g.,
user.name, not user.full_name).schema property inside the plugin config:
auth.ts
Extending the core schema
Add custom fields to theuser or session tables using additionalFields. The CLI will automatically update the database schema, and these fields will be properly inferred by TypeScript in functions like useSession and signUp.email.
auth.ts
type: Data type —"string","number","boolean","date", or a tuple of string literals for enums.required: Whether the field is required on create.defaultValue: Default value (JS layer only; the column is optional in the database).input: Whenfalse, the field cannot be provided by the user at signup (useful for fields likerole).
Mapping OAuth profiles to additional fields
UsemapProfileToUser to populate additional user fields from an OAuth provider’s profile:
auth.ts
ID generation
Better Auth generates unique string IDs for all entities by default. You can customize this behavior withadvanced.database.generateId.
Let the database generate IDs
SetgenerateId to false to let your database handle all ID generation:
auth.ts
Custom ID generation function
Returnfalse or undefined from the function to let the database generate IDs for specific models:
auth.ts
Auto-incrementing numeric IDs
SetgenerateId to "serial" for auto-incrementing numeric IDs across all tables. The CLI will generate the schema with the id field as a numeric type.
auth.ts
Better Auth infers
id as a string internally but converts to and from the numeric type when reading or writing to the database. Always pass IDs as strings to Better Auth endpoints.UUIDs
SetgenerateId to "uuid" to use UUID columns in your database. For PostgreSQL, Better Auth allows the database to generate the UUID automatically.
Database hooks
Database hooks execute custom logic before or after core database operations on the user, session, and account models.Hook types
- Before hook: Runs before create, update, or delete. Return
falseto abort the operation. Return a data object to replace the original payload. - After hook: Runs after create or update. Use for side effects like sending notifications or provisioning external resources.
auth.ts
Throwing errors from hooks
UseAPIError to abort an operation and return an error response:
auth.ts
Using the context object
Thectx object (second argument) carries useful information. For update hooks, it includes the current session:
auth.ts
Plugin schema
Plugins can define their own tables and add columns to core tables. For example, the two-factor authentication plugin addstwoFactorEnabled, twoFactorSecret, and twoFactorBackupCodes columns to the user table.
To update your schema after adding a plugin, run npx auth@latest migrate or npx auth@latest generate.
Experimental joins
Since Better Auth v1.4, you can enable experimental database joins to reduce the number of database roundtrips. Over 50 endpoints support joins.auth.ts