Overview
PDF AI uses Neon as its PostgreSQL database provider, combined with Drizzle ORM for type-safe database operations. Neon provides serverless Postgres with features like automatic scaling, branching, and generous free tier.Configuration
Environment Variables
Add your Neon database connection string to.env:
Get your connection string from the Neon Console after creating a project
Drizzle Configuration
The Drizzle configuration is defined indrizzle.config.ts:1-14:
The
dotenv package is imported to ensure environment variables are available during migration runs.Database Schema
The application uses three main tables defined insrc/lib/db/schema.ts:1-40:
Chats Table
Stores PDF chat sessions:Fields
Auto-incrementing primary key
The original filename of the uploaded PDF
The public S3 URL to access the PDF
The Clerk user ID who owns this chat
The S3 file key for downloading the PDF
Automatically set to the current timestamp on creation
Messages Table
Stores conversation messages:Relationships
- One-to-Many: Each chat can have many messages
- Foreign Key:
chatIdreferenceschats.id
Fields
Foreign key linking to the parent chat
The message text content
Either ‘user’ (human message) or ‘system’ (AI response)
User Subscriptions Table
Tracks Stripe subscription status:Database Connection
Initialize the Drizzle client:Common Operations
Querying Chats
Get all chats for a user (src/app/page.tsx:19-24):
Inserting a Chat
Querying Subscriptions
Check if user has an active subscription (src/lib/subscription.ts:11-15):
Creating Messages
Joining Tables
Get chat with all messages:Migrations
Drizzle Kit handles database migrations:Generate Migration
After modifyingschema.ts, generate a migration:
Apply Migration
Run migrations against your database:Migrations are stored in the
drizzle directory and should be committed to version control.Drizzle Studio
Browse your database with a visual interface:https://local.drizzle.studio for exploring tables and data.
Type Safety
Drizzle provides full TypeScript support:Neon Features
Serverless Architecture
- Auto-scaling: Automatically scales compute based on load
- Scale to Zero: Pauses during inactivity to save costs
- Instant Activation: Resumes in milliseconds when accessed
Branching
Create database branches for development:Connection Pooling
Neon automatically manages connection pooling for optimal performance.Dependencies
Best Practices
Connection Management
- Reuse Connections: Initialize
dbonce and import it everywhere - Serverless Optimization: Neon’s HTTP API is ideal for serverless functions
- Connection String Security: Never commit
.envfiles to version control
Schema Design
- Foreign Keys: Use
.references()for referential integrity - Indexes: Add indexes on frequently queried columns (e.g.,
userId) - Enums: Use
pgEnumfor fields with fixed values
Query Optimization
Error Handling
Troubleshooting
Connection Errors
- Verify
DATABASE_URLis correctly formatted withsslmode=require - Check that your IP is allowed in Neon’s connection settings
- Ensure the database exists and is not paused
Migration Issues
- Schema Drift: Run
drizzle-kit generateafter every schema change - Failed Migrations: Check Neon logs in the console for error details
- Breaking Changes: Use Neon branches to test migrations before applying to production
Type Errors
- Regenerate migrations after schema changes:
npx drizzle-kit generate - Restart your TypeScript server to pick up new types
- Check for mismatched imports from
schema.ts
Performance Tips
- Indexes: Add indexes on foreign keys and frequently filtered columns
- Limit Results: Use
.limit()for paginated queries - Select Specific Columns: Only select needed fields to reduce data transfer
- Connection Pooling: Neon handles this automatically for HTTP connections