Skip to main content
The database schema is defined in src/db/schema.ts using Drizzle ORM. All tables are auto-generated by Better Auth to handle authentication, sessions, and OAuth.

Schema File Location

src/db/schema.ts

Better Auth Tables

Better Auth automatically creates and manages five tables for authentication. You don’t need to manually create these tables - they’re generated when you run npm run db:push or npm run db:migrate.

User Table

Stores user account information and profiles.
src/db/schema.ts
export const user = pgTable("user", {
  id: text('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').notNull().unique(),
  emailVerified: boolean('email_verified').$defaultFn(() => false).notNull(),
  image: text('image'),
  createdAt: timestamp('created_at').$defaultFn(() => new Date()).notNull(),
  updatedAt: timestamp('updated_at').$defaultFn(() => new Date()).notNull()
});
Columns:
ColumnTypeConstraintsDescription
idtextPrimary KeyUnique user identifier
nametextNot NullUser’s display name
emailtextNot Null, UniqueUser’s email address
emailVerifiedbooleanNot Null, Default: falseEmail verification status
imagetextNullableUser’s profile image URL
createdAttimestampNot Null, AutoAccount creation timestamp
updatedAttimestampNot Null, AutoLast update timestamp

Session Table

Tracks active user sessions with device information.
src/db/schema.ts
export const session = pgTable("session", {
  id: text('id').primaryKey(),
  expiresAt: timestamp('expires_at').notNull(),
  token: text('token').notNull().unique(),
  createdAt: timestamp('created_at').notNull(),
  updatedAt: timestamp('updated_at').notNull(),
  ipAddress: text('ip_address'),
  userAgent: text('user_agent'),
  userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' })
});
Columns:
ColumnTypeConstraintsDescription
idtextPrimary KeyUnique session identifier
expiresAttimestampNot NullSession expiration time
tokentextNot Null, UniqueSession token
createdAttimestampNot NullSession creation time
updatedAttimestampNot NullLast session update
ipAddresstextNullableClient IP address
userAgenttextNullableClient user agent string
userIdtextForeign Key, Not NullReferences user.id (cascade delete)
Relationships:
  • userIduser.id (one-to-many: one user can have multiple sessions)
  • Cascade delete: deleting a user removes all their sessions

Account Table

Stores OAuth provider accounts and authentication credentials.
src/db/schema.ts
export const account = pgTable("account", {
  id: text('id').primaryKey(),
  accountId: text('account_id').notNull(),
  providerId: text('provider_id').notNull(),
  userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
  accessToken: text('access_token'),
  refreshToken: text('refresh_token'),
  idToken: text('id_token'),
  accessTokenExpiresAt: timestamp('access_token_expires_at'),
  refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
  scope: text('scope'),
  password: text('password'),
  createdAt: timestamp('created_at').notNull(),
  updatedAt: timestamp('updated_at').notNull()
});
Columns:
ColumnTypeConstraintsDescription
idtextPrimary KeyUnique account identifier
accountIdtextNot NullProvider-specific account ID
providerIdtextNot NullAuth provider (e.g., “google”, “credential”)
userIdtextForeign Key, Not NullReferences user.id (cascade delete)
accessTokentextNullableOAuth access token
refreshTokentextNullableOAuth refresh token
idTokentextNullableOAuth ID token
accessTokenExpiresAttimestampNullableAccess token expiration
refreshTokenExpiresAttimestampNullableRefresh token expiration
scopetextNullableOAuth scope
passwordtextNullableHashed password (for credential provider)
createdAttimestampNot NullAccount creation time
updatedAttimestampNot NullLast update time
Relationships:
  • userIduser.id (one-to-many: one user can have multiple provider accounts)
  • Cascade delete: deleting a user removes all their accounts

Verification Table

Stores email verification tokens and other verification codes.
src/db/schema.ts
export const verification = pgTable("verification", {
  id: text('id').primaryKey(),
  identifier: text('identifier').notNull(),
  value: text('value').notNull(),
  expiresAt: timestamp('expires_at').notNull(),
  createdAt: timestamp('created_at').$defaultFn(() => new Date()),
  updatedAt: timestamp('updated_at').$defaultFn(() => new Date())
});
Columns:
ColumnTypeConstraintsDescription
idtextPrimary KeyUnique verification identifier
identifiertextNot NullUser identifier (email, phone, etc.)
valuetextNot NullVerification token/code
expiresAttimestampNot NullToken expiration time
createdAttimestampAutoToken creation time
updatedAttimestampAutoLast update time

JWKS Table

Stores JSON Web Key Sets for JWT token verification.
src/db/schema.ts
export const jwks = pgTable("jwks", {
  id: text('id').primaryKey(),
  publicKey: text('public_key').notNull(),
  privateKey: text('private_key').notNull(),
  createdAt: timestamp('created_at').notNull()
});
Columns:
ColumnTypeConstraintsDescription
idtextPrimary KeyUnique key identifier
publicKeytextNot NullPublic key (for JWT verification)
privateKeytextNot NullPrivate key (for JWT signing)
createdAttimestampNot NullKey creation timestamp
The JWKS table is used by Better Auth to issue and verify JWTs. Your backend can fetch public keys from the /api/auth/jwks endpoint to verify tokens without sharing secrets.

Schema Relationships

The schema includes the following relationships:

Type Inference

Drizzle automatically infers TypeScript types from your schema:
import { user, session, account } from "@/db/schema";
import type { InferSelectModel, InferInsertModel } from "drizzle-orm";

// Select types (what you get from queries)
type User = InferSelectModel<typeof user>;
type Session = InferSelectModel<typeof session>;
type Account = InferSelectModel<typeof account>;

// Insert types (what you need to insert)
type NewUser = InferInsertModel<typeof user>;
type NewSession = InferInsertModel<typeof session>;
type NewAccount = InferInsertModel<typeof account>;

Extending the Schema

To add custom tables to the schema, see the Adding Tables guide.

Next Steps

Migrations

Learn how to apply schema changes

Adding Tables

Add custom tables to your database

Build docs developers (and LLMs) love