Database Schema Setup
Phase 1: Database Schema
Overview
Create the database schema for storing verification tokens. This phase sets up the foundation for secure token storage and retrieval.
Requirements
Functional:
- β’Store verification tokens linked to users
- β’Track token creation and expiry times
- β’Support efficient token lookups
Non-functional:
- β’Indexed for fast token queries
- β’Cascade delete when user deleted
- β’Unique constraint on tokens
Schema Design
Option A: Separate Tokens Table (Recommended)
Best for: Multiple token types (email, password reset, etc.)
CREATE TABLE verification_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token VARCHAR(64) UNIQUE NOT NULL,
type VARCHAR(32) NOT NULL DEFAULT 'email_verification',
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT valid_token_length CHECK (length(token) >= 32)
);
-- Index for token lookup (most common query)
CREATE INDEX idx_verification_tokens_token ON verification_tokens(token);
-- Index for user cleanup
CREATE INDEX idx_verification_tokens_user_id ON verification_tokens(user_id);
-- Index for expired token cleanup
CREATE INDEX idx_verification_tokens_expires_at ON verification_tokens(expires_at);
Option B: Add to Users Table
Best for: Simple apps with only email verification
ALTER TABLE users ADD COLUMN email_verification_token VARCHAR(64);
ALTER TABLE users ADD COLUMN email_verification_expires_at TIMESTAMP WITH TIME ZONE;
ALTER TABLE users ADD COLUMN email_verified_at TIMESTAMP WITH TIME ZONE;
CREATE INDEX idx_users_verification_token ON users(email_verification_token)
WHERE email_verification_token IS NOT NULL;
Prisma Schema (if using Prisma)
model VerificationToken {
id String @id @default(uuid())
userId String @map("user_id")
token String @unique @db.VarChar(64)
type String @default("email_verification") @db.VarChar(32)
expiresAt DateTime @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([token])
@@index([userId])
@@index([expiresAt])
@@map("verification_tokens")
}
model User {
id String @id @default(uuid())
email String @unique
emailVerifiedAt DateTime? @map("email_verified_at")
// ... other fields
verificationTokens VerificationToken[]
@@map("users")
}
Migration Commands
Prisma
npx prisma migrate dev --name add_verification_tokens
Raw SQL (PostgreSQL)
psql -d your_database -f migrations/001_verification_tokens.sql
Drizzle
npx drizzle-kit generate:pg
npx drizzle-kit push:pg
Cleanup Job (Optional)
Periodically remove expired tokens to keep table small:
-- Run via cron or scheduled job
DELETE FROM verification_tokens
WHERE expires_at < NOW() - INTERVAL '7 days';
Success Criteria
- β’ Migration created and applied
- β’ Indexes verified with EXPLAIN ANALYZE
- β’ Foreign key constraint working (test cascade)
- β’ Unique constraint on token working
Related Files
- β’Create:
prisma/schema.prisma(if Prisma) - β’Create:
migrations/001_verification_tokens.sql - β’Modify: User model to add relation