Skip to main content
Featul uses PostgreSQL with Drizzle ORM for type-safe database operations. This guide covers database provisioning, schema migrations, and production best practices.

Database Requirements

Featul requires a PostgreSQL database with the following specifications:
  • PostgreSQL Version: 14 or higher
  • Connection Method: Neon serverless HTTP or standard PostgreSQL
  • SSL: Required for production (sslmode=require)
  • Extensions: None required (uses standard PostgreSQL features)

Neon

Serverless PostgreSQL with instant branching and auto-scaling

Supabase

Open-source Firebase alternative with PostgreSQL

Railway

Developer-first cloud platform with PostgreSQL
Featul is optimized for Neon with the @neondatabase/serverless driver for HTTP-based connections.

Provisioning a Database

Database Schema

Featul’s database schema is defined in packages/db/schema/ with the following tables:

Core Tables

  • user - User accounts and profiles
  • session - Active user sessions
  • account - OAuth provider accounts
  • verification - Email verification tokens
  • passkeyTable - WebAuthn passkey credentials
  • twoFactorTable - Two-factor authentication settings

Workspace Tables

  • workspace - Multi-tenant workspaces
  • workspaceMember - Workspace membership and roles
  • workspaceDomain - Custom domain configurations
  • workspaceInvite - Pending workspace invitations
  • workspaceSlugReservation - Reserved workspace slugs

Feedback Tables

  • board - Feedback boards configuration
  • post - Feedback posts and feature requests
  • tag - Tags for categorization
  • postTag - Many-to-many post-tag relationships
  • postUpdate - Status updates on posts
  • postReport - Reported posts
  • postMerge - Merged/duplicate posts

Engagement Tables

  • comment - Comments on posts
  • commentReaction - Emoji reactions on comments
  • commentMention - User mentions in comments
  • commentReport - Reported comments
  • vote - User votes on posts

Additional Tables

  • subscription - Workspace subscriptions and billing
  • brandingConfig - Custom branding settings
  • changelogEntry - Product changelog entries
  • activityLog - Audit log for workspace activities
  • workspaceIntegration - Third-party integrations
  • workspaceNotraConnection - Notra credential connections

Running Migrations

Featul uses Drizzle Kit for schema migrations. All migration files are in packages/db/drizzle/.
Migrations are automatically generated from TypeScript schema files using Drizzle Kit.

Initial Setup

1

Install Dependencies

bun install
2

Set Database URL

Ensure DATABASE_URL is set in your environment:
export DATABASE_URL="postgresql://user:password@host:5432/database?sslmode=require"
3

Run Migrations

bun run db:migrate
This executes all SQL migration files in packages/db/drizzle/ in order.
4

Verify Schema

# Open Drizzle Studio to inspect database
bun run db:studio
Access at https://local.drizzle.studio to browse tables and data.

Development Workflow

# After modifying schema files in packages/db/schema/
bun run db:generate
db:push bypasses migrations and directly syncs schema. Only use in development. Always use db:migrate for production.

Production Migrations

For production deployments:
1

Backup Database

# Neon: Use branch feature for zero-downtime migrations
# Self-hosted: Create backup
pg_dump $DATABASE_URL > backup-$(date +%Y%m%d).sql
2

Test Migration Locally

# Clone production data to staging
# Run migration on staging first
DATABASE_URL=$STAGING_DATABASE_URL bun run db:migrate
3

Run Production Migration

# Set production DATABASE_URL
export DATABASE_URL=$PRODUCTION_DATABASE_URL

# Run migrations
bun run db:migrate
4

Verify Migration

# Check migration status
bun run db:studio

# Verify application works
curl https://app.featul.com/api/health

Drizzle Configuration

Drizzle is configured in packages/db/drizzle.config.ts:
import { defineConfig } from "drizzle-kit"
import "dotenv/config"

export default defineConfig({
  out: "./drizzle",              // Migration output directory
  schema: "./schema/index.ts",   // Schema definition files
  dialect: "postgresql",         // Database dialect
  dbCredentials: {
    url: process.env.DATABASE_URL ?? "",
  },
})
Database connection is initialized in packages/db/index.ts:
import { drizzle } from "drizzle-orm/neon-http"
import { neon } from "@neondatabase/serverless"
import * as schema from "./schema"

const DATABASE_URL = process.env.DATABASE_URL
if (!DATABASE_URL) {
  throw new Error("DATABASE_URL is not set")
}

export const db = drizzle(neon(DATABASE_URL), { schema })

Connection Pooling

For high-traffic production deployments, use connection pooling:
Neon automatically handles connection pooling with their serverless driver.Use the Pooled connection string from dashboard:
postgresql://user:[email protected]/dbname?sslmode=require
No additional configuration needed.

Database Seeding (Optional)

For development or demo environments, seed the database with sample data:
# Seed workspace posts (development only)
bun run db:seed-posts
Seeding scripts are for development only. Never run on production databases.

Monitoring and Maintenance

Query Performance

-- Find slow queries (PostgreSQL)
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

Database Size

-- Check table sizes
SELECT 
  schemaname,
  tablename,
  pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;

Backup Strategy

Implement automated backups for production databases.
Neon provides:
  • Point-in-time recovery: Restore to any point in the last 7 days (paid plans)
  • Branch feature: Create database branches for testing
  • Automated backups: Daily backups included
Create a branch for testing:
# Using Neon CLI
neon branches create --project-id PROJECT_ID --name staging

Troubleshooting

Migration Fails

Error: relation "user" does not exist
Solution: Run all migrations from the beginning:
bun run db:migrate

Connection Timeout

Error: Connection timeout
Solution:
  • Verify DATABASE_URL is correct
  • Check database is running and accessible
  • Ensure firewall allows connections
  • For Neon: Verify project is not suspended

SSL Required Error

Error: SSL required
Solution: Add ?sslmode=require to connection string:
DATABASE_URL=postgresql://user:password@host:5432/db?sslmode=require

Permission Denied

Error: permission denied for table
Solution: Grant necessary permissions:
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO featul_user;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO featul_user;

Next Steps

Environment Variables

Configure DATABASE_URL and other variables

Deploy to Vercel

Deploy your application with database configured

Build docs developers (and LLMs) love