Skip to main content
Uxie uses PostgreSQL as its database, managed through Prisma ORM. The recommended hosting solution is Supabase, which provides a managed PostgreSQL database with additional features.

Supabase Setup

1. Create a Supabase Project

  1. Go to Supabase and sign up or log in
  2. Click “New Project”
  3. Fill in your project details:
    • Name: Your project name (e.g., “uxie-production”)
    • Database Password: Generate a strong password
    • Region: Choose closest to your users
    • Pricing Plan: Start with the free tier
  4. Wait for your database to be provisioned (takes ~2 minutes)

2. Get Your Connection String

  1. In your Supabase project dashboard, go to Settings > Database
  2. Find the Connection string section
  3. Select URI format
  4. Copy the connection string
  5. Replace [YOUR-PASSWORD] with your actual database password
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@db.xxxxx.supabase.co:5432/postgres
The connection string should include ?pgbouncer=true if you’re using connection pooling. For serverless deployments, this is recommended.

Prisma Configuration

Database Provider

The Prisma schema is configured to use PostgreSQL:
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

Database Schema

Uxie’s database schema includes the following main models:
User Model
  • Stores user profile information
  • Supports three plan tiers: FREE, FREE_PLUS, PRO
  • Tracks creation date and relationships to other entities
Account Model
  • Manages OAuth provider accounts (Google)
  • Stores access tokens, refresh tokens, and session data
  • Links to User via userId
model User {
  id                        String             @id @default(cuid())
  name                      String
  email                     String?            @unique
  emailVerified             DateTime?
  image                     String?
  accounts                  Account[]
  documents                 Document[]
  messages                  Message[]
  collaboratorateddocuments Collaborator[]
  createdAt                 DateTime           @default(now())
  feedbacks                 Feedback[]
  flashcardAttempts         FlashcardAttempt[]
  plan                      Plan               @default(FREE)
}

enum Plan {
  FREE
  FREE_PLUS
  PRO
}
Document Model
  • Stores PDF documents with metadata
  • Tracks vectorization status (isVectorised)
  • Supports both uploaded and URL-based documents
  • Includes page count, cover image, and summary
Collaborator Model
  • Manages document sharing permissions
  • Three roles: EDITOR, VIEWER, OWNER
Highlight Model
  • Stores text and image highlights
  • Includes bounding rectangles and coordinates
  • Links to specific pages
model Document {
  id            String         @id @default(cuid())
  url           String
  title         String
  owner         User           @relation(fields: [ownerId], references: [id])
  ownerId       String
  note          String?        @db.Text
  isVectorised  Boolean        @default(false)
  pageCount     Int
  isUploaded    Boolean        @default(true)
  coverImageUrl String
  summary       String?        @db.Text
  lastReadPage  Int            @default(1)
  createdAt     DateTime       @default(now())
  updatedAt     DateTime       @default(now()) @updatedAt
  
  highlights    Highlight[]
  collaborators Collaborator[]
  messages      Message[]
  flashcards    Flashcard[]
}

enum CollaboratorRole {
  EDITOR
  VIEWER
  OWNER
}
Message Model
  • Stores chat messages for document discussions
  • Supports JSON-formatted message parts
  • Links to both user and document
  • Cascade deletes when document is removed
model Message {
  id         String   @id @default(cuid())
  createdAt  DateTime @default(now())
  user       User?    @relation(fields: [userId], references: [id])
  userId     String?
  document   Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
  documentId String
  parts      Json?    @db.Json
}
Flashcard Model
  • Stores AI-generated flashcards from documents
  • Includes question and answer pairs
FlashcardAttempt Model
  • Tracks user attempts at flashcards
  • Records user responses and correctness
  • Provides additional context in moreInfo
model Flashcard {
  id                String             @id @default(cuid())
  question          String
  answer            String
  document          Document           @relation(fields: [documentId], references: [id], onDelete: Cascade)
  documentId        String
  createdAt         DateTime           @default(now())
  flashcardAttempts FlashcardAttempt[]
}

model FlashcardAttempt {
  id                String    @id @default(cuid())
  flashcard         Flashcard @relation(fields: [flashcardId], references: [id], onDelete: Cascade)
  flashcardId       String
  user              User      @relation(fields: [userId], references: [id])
  userId            String
  createdAt         DateTime  @default(now())
  userResponse      String
  correctResponse   String?
  incorrectResponse String?
  moreInfo          String?
}
Feedback Model
  • Collects user feedback
  • Optional contact email for follow-up
  • Categorized by type
model Feedback {
  id            String   @id @default(cuid())
  message       String   @db.Text
  contact_email String?
  type          String
  createdAt     DateTime @default(now())
  user          User?    @relation(fields: [userId], references: [id])
  userId        String?
}

Prisma Migrations

Initial Setup

  1. Install Prisma CLI:
npm install -D prisma
  1. Generate Prisma Client:
npx prisma generate
  1. Run migrations:
npx prisma migrate dev --name init

Development Workflow

  1. Make schema changes in prisma/schema.prisma
  2. Create migration:
npx prisma migrate dev --name descriptive_name
  1. Apply to production:
npx prisma migrate deploy

Useful Commands

prisma studio
command
Open Prisma Studio to view and edit your database in a GUI
npx prisma studio
prisma db push
command
Push schema changes directly to database without creating migration files (useful for prototyping)
npx prisma db push
prisma migrate reset
command
Reset database and apply all migrations from scratch
npx prisma migrate reset
This will delete all data in your database. Only use in development.

Connection String Format

The PostgreSQL connection string follows this format:
postgresql://[USER]:[PASSWORD]@[HOST]:[PORT]/[DATABASE]?[OPTIONS]
Example with options:
postgresql://postgres:[email protected]:5432/postgres?schema=public&pgbouncer=true&connection_limit=1
Common options:
  • schema=public - PostgreSQL schema to use
  • pgbouncer=true - Use connection pooling
  • connection_limit=1 - Limit concurrent connections (recommended for serverless)

Troubleshooting

If you’re getting connection timeouts:
  1. Check your database is running in Supabase dashboard
  2. Verify your IP is allowed (Supabase allows all IPs by default)
  3. Ensure connection string includes correct password
  4. Try adding ?connect_timeout=10 to connection string
If you’re hitting connection limits:
  1. Enable connection pooling: ?pgbouncer=true
  2. Set connection limit: ?connection_limit=1
  3. Close unused Prisma Client instances
  4. Use Prisma’s recommended connection pattern:
import { PrismaClient } from '@prisma/client'

const globalForPrisma = global as unknown as { prisma: PrismaClient }

export const prisma =
  globalForPrisma.prisma || new PrismaClient()

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
If Prisma Client is out of sync with database:
  1. Regenerate Prisma Client: npx prisma generate
  2. Pull current database schema: npx prisma db pull
  3. Create new migration: npx prisma migrate dev

Build docs developers (and LLMs) love