Documentation/Database & Drizzle CLI Guide
Database Architecture & CLI Suite

THUNDER Database & Drizzle Guide

Complete guide to managing PostgreSQL databases, Drizzle ORM, schema migrations, Aiven SSL connection handling, and custom CLI automation scripts.

PostgreSQL Datastore

Supports Aiven PostgreSQL, Neon serverless, Supabase, or local PostgreSQL containers seamlessly.

Drizzle ORM

100% TypeScript type safety with zero runtime code generation overhead.

Aiven SSL Fix Built-in

Automatic SSL certificate resolution (rejectUnauthorized: false) for Cloudflare Workers.

Complete CLI Suite

Dedicated status checking, seeding, migration rollback, and database reset commands.

Database CLI Management Commands

Run these commands from the root directory to manage migrations, seed data, or run diagnostics.

pnpm db:statusDiagnostics

Migration & System Health Status

Displays current database migration state, counts applied vs pending migrations, verifies active database connections, and checks SSL handshake stability.

$ pnpm db:status
[DB Status] Checking database migration status...
[DB Status] Connected to PostgreSQL: postgresql://*****@aiven-db.com:25432/defaultdb
[DB Status] Current migration version: 20260729_init
[DB Status] Applied migrations: 3 / 3
[DB Status] Pending migrations: 0
[DB Status] System Health: OK
pnpm db:migrateDeployment

Apply Pending Migrations

Executes all pending Drizzle SQL migration files sequentially inside a database transaction.

$ pnpm db:migrate
[DB Migrate] Applying pending migrations...
[DB Migrate] Executing migration: 0001_add_rbac_tables.sql
[DB Migrate] Migrations completed successfully in 142ms.
pnpm db:seedData Setup

Seed Default Roles & Users

Seeds default system roles (Admin, Manager, User), granular permissions, and creates default administrator credentials.

$ pnpm db:seed
[DB Seed] Seeding database...
[DB Seed] Created roles: admin, manager, user
[DB Seed] Assigned 24 RBAC permissions
[DB Seed] Default admin created: admin@thunderstack.dev
pnpm db:rollbackRecovery

Roll Back Migrations

Safely rolls back the last applied migration batch, updating migration metadata tables accurately.

$ pnpm db:rollback
[DB Rollback] Reverting last migration batch...
[DB Rollback] Rolled back: 0001_add_rbac_tables.sql
[DB Rollback] Database state restored to version 0000_init.sql
pnpm db:resetDangerous / Dev

Wipe & Re-initialize Database

Drops all existing database schema tables, re-applies all Drizzle migrations from scratch, and runs the seed script.

$ pnpm db:reset
[DB Reset] WARNING: Dropping all database tables...
[DB Reset] Schema dropped successfully.
[DB Reset] Running pnpm db:migrate...
[DB Reset] Running pnpm db:seed...
[DB Reset] Database reset complete!
pnpm db:helpUtility

Interactive Command Manual

Launches the interactive CLI manual explaining all available database options, parameters, and flags.

$ pnpm db:help
===================================================
   ⚡ THUNDER STACK DATABASE UTILITIES MANUAL ⚡
===================================================
Available commands:
  pnpm db:status    - Check migration status & SSL
  pnpm db:migrate   - Apply pending migrations
  pnpm db:seed      - Seed default RBAC roles & admin
  pnpm db:rollback  - Revert last migration batch
  pnpm db:reset     - Clean rebuild database tables
  pnpm db:generate  - Generate Drizzle SQL migration
  pnpm db:studio    - Open visual Drizzle Studio GUI

Database Schema & Models

Core entities defined in server/db/src/schema.ts.

Table NameDescriptionColumns
usersCore user entity storing profiles, email verification status, and RBAC role assignment.id, name, email, emailVerified, image, roleId, createdAt, updatedAt
rolesRBAC Role definitions (e.g., Admin, Manager, User) supporting system-level locking.id, name, description, isSystem, createdAt, updatedAt
permissionsGranular permission definitions (e.g., users:read, users:write, settings:manage).id, key, name, description, category
role_permissionsJunction table mapping roles to multiple permissions.roleId, permissionId
sessionsActive authentication sessions managed by Better Auth with IP and user-agent tracking.id, userId, token, expiresAt, ipAddress, userAgent
accountsOAuth provider accounts (Google, GitHub) connected to user profiles.id, userId, accountId, providerId, accessToken, refreshToken
verificationsEmail verification tokens and 6-digit OTP codes for passwordless or email auth.id, identifier, value, expiresAt

Aiven PostgreSQL SSL Connection Setup

Connecting Cloudflare Workers & Node.js to cloud PostgreSQL databases with SSL verification.

When connecting to cloud PostgreSQL providers such as Aiven PostgreSQL, SSL mode is mandatory. In serverless environments like Cloudflare Workers (wrangler), default SSL CA certificate bundles can cause handshake errors. THUNDER Stack automatically configures the connection driver:

// server/db/src/index.ts
import { drizzle } from "drizzle-orm/node-postgres";
import pg from "pg";

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  ssl: process.env.DATABASE_URL?.includes("sslmode=require")
    ? { rejectUnauthorized: false }
    : undefined,
});

export const db = drizzle(pool);

Production Scenarios & Conflict Resolution

Common challenges faced during production database operations and how to resolve them safely.

1Adding a "NOT NULL" Column to a Table with Existing Data

The Issue: If you add a new column defined as notNull() to an active table that already has rows of data, PostgreSQL will reject the migration with a constraint violation because existing rows cannot accept null values.

How to tackle it:

  1. Define a default value: Always supply a default fallback directly in your Drizzle schema, e.g. .default("default_val") or .default(0).
  2. Three-Step Migration (No default):
    • Generate a migration adding the column as nullable first.
    • Run an SQL update script to populate values for existing rows.
    • Generate a final migration altering the column to enforce NOT NULL.

2Data Loss Risk During Migration Rollbacks

The Issue: Running pnpm db:rollback drops the tables or columns created by the target migration. If this command is executed in staging or production, any data stored in those columns will be permanently deleted.

How to tackle it:

  1. Execute Backup: Always perform a snapshot backup using database utilities (e.g., pg_dump) prior to performing rollbacks.
  2. Avoid Rollbacks in Production: Prefer "roll-forward" actions. If you need to revert a change in production, generate a brand new migration file that safely deletes the column or drops the table, allowing for data migration steps before the drop.

3Database Table Locks & Connection Timeouts

The Issue: Performing schema alterations (like adding foreign keys or indexes) on large tables locks the table. Active API threads attempting to query this table will hang and timeout, exhausting the database connection pool.

How to tackle it:

  1. Create Indexes Concurrently: For high-traffic tables, edit the generated SQL file manually to apply indexes concurrently: CREATE INDEX CONCURRENTLY.
  2. Schedule Off-Peak Hours: Apply intensive migrations when database workload is lowest.

4Out-of-Sync Migration Metadata Table

The Issue: If a local migration SQL file is modified after it has already been applied, the database's internal metadata tracking table (drizzle.__drizzle_migrations) will mismatch and block future deployments.

How to tackle it:

  1. Diagnose Status: Run pnpm db:status to isolate exactly which migration checksum is failing.
  2. Manually Adjust Checksum: If the database and schema are physically identical, update the failing checksum row in the drizzle.__drizzle_migrations table, or delete the record of the target migration and run `db:migrate` again.

Ready to deploy your frontend & backend?

Check out our complete deployment guide for Vercel, Cloudflare Pages, and Cloudflare Workers.

View Deployment Guide