7.3 KiB
03 — Database Schema
ORM: Prisma
Database: PostgreSQL
Schema file: prisma/schema.prisma
Entity Relationship Diagram
User
├──< Contract (one user → many contracts)
│ ├──< ContractRagChunk (one contract → many RAG chunks)
│ ├──< Notification (one contract → many notifications)
│ └──< BlockchainTransaction (one contract → many blockchain txs)
├──< Notification (one user → many notifications)
└──< BlockchainTransaction (one user → many blockchain txs)
Models
User
Stores platform users, synchronized from Clerk via webhook.
| Column | Type | Notes |
|---|---|---|
id |
String (cuid) |
Primary key |
clerkId |
String |
Unique Clerk user ID |
email |
String |
Unique email address |
firstName |
String? |
Optional first name |
lastName |
String? |
Optional last name |
imageUrl |
String? |
Clerk profile image URL |
createdAt |
DateTime |
Record creation timestamp |
updatedAt |
DateTime |
Auto-updated on changes |
Indexes: clerkId, email
Sync: Created/updated by app/api/webhooks/clerk/ in response to Clerk user.created and user.updated events.
Contract
The core entity — one record per uploaded document.
File Information (from upload):
| Column | Type | Notes |
|---|---|---|
id |
String (cuid) |
Primary key |
userId |
String |
FK → User.id (cascade delete) |
fileName |
String |
Original file name |
fileUrl |
String |
UploadThing CDN URL |
fileSize |
Int |
File size in bytes |
mimeType |
String |
application/pdf or image/* |
AI-Extracted Fields (filled after analysis):
| Column | Type | Notes |
|---|---|---|
title |
String? |
Contract title |
type |
ContractType? |
Enum (see below) |
provider |
String? |
Insurance company / bank name |
policyNumber |
String? |
Policy or contract reference number |
startDate |
DateTime? |
Contract start date |
endDate |
DateTime? |
Contract end / expiry date |
premium |
Decimal(10,2)? |
Annual/monthly premium amount |
extractedText |
String? (Text) |
Full text extracted from document |
summary |
String? (Text) |
4-6 sentence AI summary |
keyPoints |
Json? |
Guarantees, exclusions, explainability |
Processing:
| Column | Type | Notes |
|---|---|---|
status |
ContractStatus |
Default: UPLOADED |
Blockchain Proof:
| Column | Type | Notes |
|---|---|---|
documentHash |
String? |
SHA-256 hash of the file (0x-prefixed) |
txHash |
String? |
Ethereum transaction hash |
blockNumber |
Int? |
Block number where tx was mined |
blockTimestamp |
DateTime? |
Block timestamp = legal proof date |
blockchainNetwork |
String? |
"hardhat" or "sepolia" |
contractAddress |
String? |
DocumentRegistry contract address |
Indexes: userId, status, type, endDate
Relations: ContractRagChunk[], Notification[], BlockchainTransaction[]
ContractRagChunk
Stores text chunks and their vector embeddings for RAG (Retrieval-Augmented Generation).
| Column | Type | Notes |
|---|---|---|
id |
String (cuid) |
Primary key |
contractId |
String |
FK → Contract.id (cascade delete) |
chunkIndex |
Int |
Position in the document |
content |
String |
Raw text content of the chunk |
contentHash |
String |
SHA-256 of content (deduplication) |
embedding |
Float[] |
Gemini text-embedding-004 vector |
createdAt |
DateTime |
— |
updatedAt |
DateTime |
— |
Unique constraint: (contractId, chunkIndex) — no duplicate chunks per contract.
Indexes: contractId, contentHash, chunkIndex
Note: PostgreSQL stores
Float[]as a native array column. Cosine similarity is computed in application code, not via pgvector. A future enhancement could migrate to pgvector for large-scale similarity search.
Notification
In-app notifications for users.
| Column | Type | Notes |
|---|---|---|
id |
String (cuid) |
Primary key |
userId |
String |
FK → User.id (cascade delete) |
contractId |
String? |
Optional FK → Contract.id (set null on delete) |
type |
NotificationType |
Enum: SUCCESS, WARNING, ERROR, INFO, DEADLINE |
title |
String |
Short notification title |
message |
String |
Full notification message |
icon |
String? |
Icon identifier for UI rendering |
actionType |
String? |
e.g., "RENEWAL_REMINDER", "UPLOAD_SUCCESS" |
actionData |
Json? |
Additional action-specific payload |
read |
Boolean |
Default: false |
createdAt |
DateTime |
— |
expiresAt |
DateTime? |
Optional expiration for auto-cleanup |
Indexes: userId, contractId, type, read, createdAt
BlockchainTransaction
Audit log of every on-chain document registration.
| Column | Type | Notes |
|---|---|---|
id |
String (cuid) |
Primary key |
userId |
String |
FK → User.id (cascade delete) |
contractId |
String |
FK → Contract.id (cascade delete) |
documentHash |
String |
SHA-256 hash of the document |
txHash |
String (unique) |
Ethereum transaction hash |
blockNumber |
Int |
Block number where tx was mined |
blockTimestamp |
DateTime |
Block timestamp |
network |
String |
"hardhat" or "sepolia" |
contractAddress |
String |
Smart contract address |
status |
String |
Default: "CONFIRMED" (PENDING/CONFIRMED/FAILED) |
createdAt |
DateTime |
— |
Indexes: userId, contractId, txHash, network
Enums
ContractType
| Value | Description |
|---|---|
INSURANCE_AUTO |
Auto/vehicle insurance |
INSURANCE_HOME |
Home/property insurance |
INSURANCE_HEALTH |
Health insurance |
INSURANCE_LIFE |
Life insurance |
LOAN |
Bank loan / mortgage |
CREDIT_CARD |
Credit card agreement |
INVESTMENT |
Investment / fund agreement |
OTHER |
Any other contract type |
ContractStatus
| Value | Description |
|---|---|
UPLOADED |
File received, awaiting analysis |
PROCESSING |
AI analysis in progress |
COMPLETED |
Analysis done and blockchain registered |
FAILED |
Analysis or registration failed |
NotificationType
| Value | Description |
|---|---|
SUCCESS |
Successful operation (analysis complete, etc.) |
WARNING |
Non-critical issue or approaching deadline |
ERROR |
Failed operation |
INFO |
General informational message |
DEADLINE |
Contract expiry deadline approaching |
Database Operations
Migrations
# Create a new migration in development
npm run db:migrate:dev -- --name <migration_name>
# Apply pending migrations in production
npm run db:migrate
# Regenerate Prisma client after schema changes
npm run db:generate
Prisma Studio (visual DB browser)
npm run db:studio
Connection Pooling (Production)
For high-traffic production deployments, use PgBouncer or Prisma Accelerate to pool PostgreSQL connections. The DATABASE_URL format with connection pooling:
DATABASE_URL="postgresql://USER:PASS@HOST:PORT/DB?pgbouncer=true&connection_limit=1"
Set connection_limit=1 when using PgBouncer in transaction mode.