Database Schema Overview

Understand the database schema structure, tables, relationships, and multi-tenancy model.

The SaaS Kit uses a multi-tenant database schema with organization-based isolation. All tables are defined using Prisma ORM.

Core Schema

The core schema is defined in the packages/database/src/prisma/schema.prisma file, where the kit defines the core models and relationships.

Extending the Schema

You can extend the schema by adding your own Prisma model definitions to the packages/database/src/prisma/schema.prisma file:

packages/database/src/prisma/schema.prisma

// Add your own models here
model Project {
id String @id
organizationId String @map("organization_id")
name String
createdAt DateTime @default(now()) @map("created_at")
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
@@map("projects")
}

Authentication Tables

The authentication tables contain the tables for the authentication system as defined in Better Auth.

Users

The table user stores user accounts and profile data.

ColumnTypeDescription
idtextPrimary key
nametextDisplay name
emailtextUnique email address
createdAttimestampUser creation date
updatedAttimestampUser last update date
banReasontextReason for ban
banExpirestimestampBan expiration date
emailVerifiedbooleanEmail verification status
imagetextProfile picture URL
twoFactorEnabledboolean2FA enabled
roletextAdmin role (for admin features)
bannedbooleanBan status
customer_idtextBilling provider customer ID

Sessions

The table session stores authentication sessions.

ColumnTypeDescription
idtextPrimary key
tokentextUnique session token
userIdtextFK to users (cascade delete)
expiresAttimestampSession expiration
createdAttimestampSession creation date
updatedAttimestampSession last update date
ipAddresstextClient IP address
userAgenttextBrowser user agent
impersonatedBytextAdmin user ID if impersonating
activeOrganizationIdtextCurrent organization context

Accounts

The table account stores OAuth provider accounts and password credentials.

ColumnTypeDescription
idtextPrimary key
providerIdtextProvider name (google, github, credential)
accountIdtextProvider's account ID
userIdtextFK to users (cascade delete)
accessTokentextOAuth access token
refreshTokentextOAuth refresh token
passwordtextHashed password (for credential auth)
idTokentextOAuth ID token
accessTokenExpiresAttimestampOAuth access token expiration
refreshTokenExpiresAttimestampOAuth refresh token expiration
scopetextOAuth scope
createdAttimestampAccount creation date
updatedAttimestampAccount last update date

Verifications

The table verification stores email verification and password reset tokens.

ColumnTypeDescription
idtextPrimary key
identifiertextEmail or phone number
valuetextVerification code/token
expiresAttimestampToken expiration
createdAttimestampVerification creation date
updatedAttimestampVerification last update date

Two Factors

The table twoFactor stores TOTP 2FA secrets and backup codes.

ColumnTypeDescription
idtextPrimary key
secrettextTOTP secret
backupCodestextBackup codes
userIdtextFK to users (cascade delete)

Organization Tables

Organizations

The table organization stores team/workspace containers.

ColumnTypeDescription
idtextPrimary key
nametextOrganization name
slugtextUnique URL-friendly slug
logotextLogo URL
metadatatextJSON metadata
createdAttimestampOrganization creation date
stripeCustomerIdtextStripe customer ID

Members

The table member links users to organizations with roles.

ColumnTypeDescription
idtextPrimary key
organizationIdtextFK to organizations (cascade delete)
userIdtextFK to users (cascade delete)
roletextRole (owner, admin, member)
createdAttimestampMember creation date

Invitations

The table invitation tracks pending organization invitations.

ColumnTypeDescription
idtextPrimary key
organizationIdtextFK to organizations (cascade delete)
emailtextInvitee's email
roletextRole to assign when accepted
statustextpending, accepted, or declined
expiresAttimestampInvitation expiration
inviterIdtextFK to users (cascade delete)
createdAttimestampInvitation creation date

Organization Roles

The table organizationRole stores custom RBAC permission definitions per organization.

ColumnTypeDescription
idtextPrimary key
organizationIdtextFK to organizations (cascade delete)
roletextRole name
permissiontextPermission name
metadatatextJSON metadata
createdAttimestampOrganization role creation date
updatedAttimestampOrganization role last update date

Billing Tables

Subscriptions

The table subscription stores billing and subscription data.

ColumnTypeDescription
idtextPrimary key
plantextPlan identifier
referenceIdtextOrganization/user reference
customerIdtextBilling provider customer ID
subscriptionIdtextBilling provider subscription ID
statustextactive, trialing, canceled, etc.
periodStarttimestampCurrent billing period start
periodEndtimestampCurrent billing period end
seatsintegerNumber of seats
trialStarttimestampTrial start date
trialEndtimestampTrial end date
cancelAtPeriodEndbooleanCancel at end of period flag
cancelAttimestampScheduled cancellation date
canceledAttimestampWhen subscription was canceled
endedAttimestampWhen subscription ended

Multi-Tenancy Model

All tenant data is isolated by organization. When adding custom tables, always include an organizationId foreign key.

For example, the model Project could be defined as follows, where organizationId is a foreign key to the Organization model:

model Project {
id String @id
organizationId String @map("organization_id")
name String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
@@map("projects")
}

Note: Always make sure to filter queries by organizationId to ensure data isolation:

// Correct - respects multi-tenancy
const projects = await db.project.findMany({
where: { organizationId: currentOrgId }
});

Index Suggestions

Core tables already include PK/unique/FK indexes. If query patterns match, consider adding:

TableSuggested indexWhy
accountunique(provider_id, account_id)Fast provider lookup, prevent dup accounts
verificationindex(identifier, value)Fast token lookup by identifier + code
organizationindex(stripe_customer_id)Resolve org from billing webhooks
organization_roleindex(organization_id, role) or unique(organization_id, role, permission)Role permission fetch + dedup
memberunique(organization_id, user_id)Fast membership lookup, prevent dup
invitationindex(organization_id, status) (opt: email, organization_id, status)Pending invites list + accept flow
subscriptionindex(reference_id), index(stripe_customer_id), index(stripe_subscription_id)App + webhook lookups
rate_limitunique(key)Upsert by key, avoid full scan

Next: Migrations →