think
16px
820px

Panduan: Cara Kerja Database dan Migrasi Prisma

Untuk: siapa pun yang baru masuk ke proyek ini dan bingung kenapa "sudah di-push
tapi punya saya error"
Tanggal: 2026-08-24


1. Dua hal yang sering tercampur: struktur dan isi

Bayangkan database sebagai lemari arsip.

Analogi lemari Di database
Struktur ada berapa laci, tiap laci diberi label apa ada tabel apa, tiap tabel punya kolom apa
Isi berkas-berkas di dalam laci baris-baris datanya

Dua hal ini nasibnya berbeda, dan itu sumber hampir semua kebingungan:

  • Struktur harus sama di semua tempat. Kalau kode mencari laci berlabel
    badanHukumNama dan lacinya tidak ada, program berhenti.
  • Isi tidak pernah sama, dan memang tidak boleh. Berkas percobaan di lemari
    Anda bukan urusan lemari orang lain.

Di proyek ini ada enam lemari terpisah, satu per developer:

ahu_ocr_dev1   ahu_ocr_dev2   ahu_ocr_dev3
ahu_ocr_dev4   ahu_ocr_dev5   ahu_ocr_dev6

Semuanya menumpang satu container Postgres yang sama, tapi isinya sendiri-sendiri.
Ditambah kembarannya untuk test (_test), plus satu lagi di staging.


2. Kenapa perlu migrasi sama sekali

Kode dan database itu dua benda terpisah yang harus saling cocok.

Git bisa memindahkan kode. Git tidak bisa memindahkan database, karena
database bukan berkas yang ikut di-commit; ia proses yang hidup di suatu tempat
dengan datanya sendiri.

Jadi ketika Anda menambah kolom, ada dua pekerjaan berbeda:

  1. Mengubah kode supaya tahu kolom itu ada. Ini ikut git.
  2. Mengubah database supaya kolomnya benar-benar ada. Ini tidak ikut git.

Migrasi adalah jembatan antara keduanya: sebuah berkas resep yang ikut git,
berisi perintah untuk mengubah database. Resepnya berpindah, masakannya tidak.
Setiap orang memasak sendiri di lemarinya masing-masing.

flowchart LR A["Anda ubah schema.prisma"] --> B["Tulis berkas migrasi
(resep SQL)"] B --> C["git push"] C --> D["Kode + resep sampai
ke semua orang"] D --> E["Tiap orang menjalankan
resep di database sendiri"] E --> F["Struktur semua orang
akhirnya sama"]

3. Tiga berkas yang berperan

Ini yang paling sering tertukar. Semuanya "tentang database", tapi perannya beda.

Berkas Apa isinya Siapa yang membuat
prisma/schema.prisma Cetak biru. Deskripsi bentuk database yang Anda inginkan Anda, ditulis tangan
prisma/migrations/*/migration.sql Resep perubahan. Perintah SQL untuk berpindah dari bentuk lama ke bentuk baru Prisma, atau ditulis tangan
src/generated/prisma/ Penerjemah. Kode TypeScript supaya db.suratKeterangan.findMany() bisa ditulis dengan aman Prisma, otomatis

Hubungannya:

flowchart TD S["schema.prisma
(cetak biru)"] S -->|"prisma migrate dev"| M["migration.sql
(resep)"] S -->|"prisma generate"| G["generated client
(penerjemah)"] M -->|"dijalankan"| D[("Database sungguhan")] G -.->|"dipakai kode Anda"| D

Perhatikan: prisma generate tidak menyentuh database sama sekali. Ia cuma
membaca cetak biru lalu menulis kode TypeScript. Aman dijalankan kapan saja.


4. Alur normal sebuah perubahan

Misalnya Anda ingin menambah kolom nomorTelepon ke tabel Notaris.

sequenceDiagram participant Anda participant Schema as schema.prisma participant Migrasi as migration.sql participant DB as Database Anda participant Git Anda->>Schema: tambah baris nomorTelepon String? Anda->>Migrasi: buat resep ALTER TABLE ... ADD COLUMN Anda->>DB: jalankan resepnya Anda->>Anda: prisma generate (perbarui penerjemah) Anda->>Anda: tulis kode yang memakai kolom baru Anda->>Git: commit + push (kode + schema + resep)

Yang penting: ketiganya harus berpindah bersamaan. Kalau kode yang memakai
kolom baru sampai ke orang lain tanpa resepnya, mereka error.


5. Dua alat, dan kapan pakai yang mana

Ini bagian yang paling sering salah kaprah.

prisma db push — "samakan saja langsung"

Prisma melihat cetak biru, melihat database Anda, lalu menghitung sendiri
selisihnya
dan langsung menerapkannya.

  • Tidak membuat berkas resep apa pun.
  • Cepat, enak untuk coba-coba.
  • Tidak meninggalkan jejak, jadi orang lain tidak tahu apa yang berubah.

prisma migrate deploy — "jalankan resep yang tertulis"

Prisma membaca folder prisma/migrations/, melihat resep mana yang belum
dijalankan, lalu menjalankannya berurutan.

  • Butuh berkas resep.
  • Bisa diulang di mana saja dengan hasil yang sama persis.
  • Dipakai staging dan produksi.
flowchart TD subgraph push["db push"] P1["Baca schema.prisma"] --> P2["Lihat database sekarang"] P2 --> P3["Tebak selisihnya"] P3 --> P4["Terapkan langsung"] end subgraph deploy["migrate deploy"] D1["Baca folder migrations/"] --> D2["Cek catatan:
resep mana yang sudah jalan"] D2 --> D3["Jalankan yang belum,
berurutan"] end

Aturan praktisnya: pakai db push untuk eksperimen di lemari sendiri. Begitu
perubahan itu akan dipakai orang lain, ia wajib punya berkas resep.


6. _prisma_migrations: buku catatan

Supaya migrate deploy tahu resep mana yang sudah dijalankan, Prisma menyimpan
sebuah tabel khusus di dalam database itu sendiri, namanya _prisma_migrations.
Isinya cuma daftar nama berkas plus kapan dijalankan.

Yang perlu dipahami, dan ini sering disalahpahami:

Prisma melacak riwayat, bukan bentuk.

Prisma tidak pernah memeriksa apakah tabel Anda sungguh punya kolom yang
seharusnya. Ia hanya membaca buku catatan itu. Kalau tertulis "resep 1 sampai 49
sudah jalan", ia percaya.

Akibatnya, kalau buku catatannya hilang atau tidak pernah ada, tapi tabelnya jelas
sudah ada, Prisma menghadapi dua fakta yang bertentangan dan menolak menebak.
Ia berhenti dengan pesan P3005, "the database schema is not empty". Itu perilaku
yang benar: menebak di sini bisa berarti menjalankan CREATE TABLE di atas tabel
yang sudah berisi data.


7. Kenapa "ganti nama kolom" berbeda dari yang lain

Hampir semua perubahan sifatnya menambah: tambah tabel, tambah kolom, tambah
nilai enum. Untuk yang menambah, db push aman, karena tidak ada yang dihapus
sehingga tidak ada yang bisa hilang.

Mengganti nama kolom lain ceritanya. Alat pembanding melihat:

  • kolom perseroanId tidak ada lagi di cetak biru
  • kolom badanHukumId muncul

dan tidak punya cara untuk tahu bahwa keduanya benda yang sama. Manusia tahu
itu ganti nama karena tahu maksudnya. Program pembanding tidak. Maka ia
menyimpulkan yang paling harfiah:

ALTER TABLE "SuratKeterangan"
  DROP COLUMN "perseroanId",       -- isinya hilang
  ADD COLUMN  "badanHukumId" TEXT NOT NULL;

Padahal yang benar, dan yang mempertahankan seluruh isinya:

ALTER TABLE "SuratKeterangan" RENAME COLUMN "perseroanId" TO "badanHukumId";

Karena itu resep untuk ganti nama harus ditulis tangan, tidak boleh
diserahkan ke generator.


8. Keadaan proyek ini sekarang

Fakta yang perlu Anda tahu, hasil pemeriksaan 2026-08-24:

Hal Keadaan
Jumlah berkas migrasi 49
Migrasi yang menghapus atau mengganti nama sesuatu 0 dari 49
Tabel _prisma_migrations di database dev tidak ada
CI yang memeriksa otomatis tidak ada

Artinya: keenam database developer selama ini disinkronkan dengan db push, dan
itu kebetulan aman karena 49 migrasi sebelumnya semuanya cuma menambah.
Bukan karena praktiknya benar, tapi karena belum pernah diuji perubahan yang
berbahaya.

Migrasi ganti nama pertama baru muncul sekarang. Itu sebabnya kebiasaan lama
tiba-tiba jadi berbahaya, dan kenapa pesan "jangan db push untuk yang ini"
perlu disampaikan ke tim.


9. Baseline: cara menghentikan masalah ini untuk selamanya

Karena buku catatan _prisma_migrations tidak ada, migrate deploy tidak bisa
dipakai di mesin dev. Perbaikannya sekali seumur hidup per database, namanya
baseline: tuliskan ke buku catatan bahwa 49 resep lama sudah dijalankan.

Yang penting dipahami: baseline tidak menjalankan SQL apa pun dan tidak
menyentuh satu baris data pun
. Ia cuma menyamakan catatan dengan kenyataan yang
memang sudah begitu.

flowchart LR A["Database dev
tabel lengkap,
catatan kosong"] --> B["Baseline:
tandai 49 resep
sebagai sudah jalan"] B --> C["Database dev
tabel lengkap,
catatan lengkap"] C --> D["migrate deploy
sekarang bisa dipakai"] D --> E["Bisa dipasang di skrip start,
tidak perlu diingat manusia"]

Setelah baseline, alurnya jadi: siapa pun git pull, jalankan migrate deploy
(atau biarkan skrip start yang melakukannya), dan database mereka menyusul
sendiri. Pertanyaan "siapa yang harus menjalankan migrasi" hilang.


10. Ringkasan yang bisa ditempel di dinding

  1. Struktur menyatu, isi tidak. Itu memang tujuannya.
  2. Git memindahkan resep, bukan database. Tiap orang memasak sendiri.
  3. Kode baru + database lama = error. Itu sinyal "jalankan migrasi", bukan kerusakan.
  4. prisma generate aman, ia tidak menyentuh database.
  5. Menambah itu jinak, mengganti nama dan menghapus itu berbahaya.
  6. Prisma melacak riwayat, bukan bentuk. Ia percaya buku catatan, bukan matanya.
  7. Kalau perubahan akan dipakai orang lain, ia wajib punya berkas migrasi.