pgrs: Bikin CLI PostgreSQL Sendiri, dan Apa yang Saya Pelajari

@fakhrulnugrohoJuly 6, 2026

Saya termasuk orang yang kerja harian dengan beberapa database PostgreSQL sekaligus — local, staging, production, plus database beberapa klien. Setiap kali mau ngecek data, ritualnya selalu sama: buka terminal, ketik ulang psql -h host -U user -d dbname, lupa password, buka password manager, copy-paste, baru bisa connect. Kalau environment-nya ada lima-enam, ritual ini terjadi berkali-kali sehari.

Lama-lama gatel. Kenapa nggak simpan saja koneksi-koneksi itu dengan nama yang gampang diingat, lalu tinggal panggil namanya? Dari rasa gatel itulah lahir pgrs — CLI dan REPL PostgreSQL untuk orang yang malas mengetik ulang connection string.

Idenya: koneksi tersimpan, bukan connection string yang diketik ulang

Hal pertama yang saya putuskan: cukup simpan koneksi sekali dengan nama, lalu panggil namanya kapan saja. Detail host, user, password, port, mode TLS — semua disimpan, bukan diketik ulang.

BASH
pgrs add prod-db --host=db.example.com --username=postgres \
  --password=secret --database=mydb --tls=verify-full --env=production

pgrs connect prod-db

pgrs connect cukup meng-exec langsung ke psql — proses digantikan sepenuhnya, tanpa lapisan tambahan. Tapi saya juga ingin sesuatu yang lebih dari sekadar shortcut ke psql: autocomplete tabel/kolom, syntax highlighting, riwayat query yang tersimpan. Jadi ada mode kedua, pgrs shell, yang membuka REPL SQL bawaan:

BASH
$ pgrs shell prod-db
pgrs(prod-db:production)> select id, email from users limit 1;

 id | email
----+------------------
  1 | user@example.com

pgrs(prod-db:production)>

Satu tool, dua cara pakai — connect untuk yang sudah biasa dengan psql, shell untuk sesi kerja yang lebih lama di satu database.

Transaction guard: mengerem sebelum melangkah

Ada satu titik ketika saya sadar REPL ini sudah cukup nyaman dipakai untuk hal yang seharusnya bikin was-was: menjalankan UPDATE atau DELETE langsung dari terminal tanpa jaring pengaman. Satu typo di klausa WHERE, dan seluruh tabel bisa berubah.

Jawabannya bukan fitur baru, tapi sebuah batasan: INSERT/UPDATE/DELETE (termasuk yang dibungkus CTE) ditolak kecuali ada transaksi terbuka lebih dulu.

BASH
pgrs(prod-db:production)> delete from users where id = 1;
error: INSERT/UPDATE/DELETE requires an explicit transaction. Run BEGIN (or \begin) first.

pgrs(prod-db:production)> \begin
BEGIN
pgrs(prod-db:production)*> delete from users where id = 1;
DELETE 1
pgrs(prod-db:production)*> \commit
COMMIT

Prompt-nya sendiri berubah jadi *> begitu transaksi terbuka, jadi status transaksi selalu kelihatan tanpa perlu ditanyakan. pgrs connect (jalur psql langsung) sengaja tidak kena aturan ini — itu jalur untuk yang sudah sadar risikonya. Menambah fitur itu menyenangkan; menambah pengereman yang tepat di tool yang menyentuh data produksi itu yang justru terasa seperti kerja "beneran".

Autocomplete yang paham konteks, dan riwayat yang tidak pernah hilang

Autocomplete di pgrs bukan sekadar daftar kata kunci SQL. Ia membaca konteks: setelah FROM/JOIN ia menyarankan nama tabel, setelah SELECT/WHERE/ON ia menyarankan kolom — termasuk kolom milik tabel yang di-alias di klausa JOIN. Saran itu juga diurutkan berdasarkan frekuensi pemakaian, jadi tabel dan kolom yang sering saya sentuh muncul lebih dulu, bukan sekadar urutan alfabet.

Hal kecil lain yang ternyata paling sering saya rasakan manfaatnya: riwayat baris REPL (panah-atas untuk recall, Ctrl+R untuk reverse-search) disimpan per koneksi ke disk, dan ditulis ulang setelah setiap perintah — bukan hanya saat sesi ditutup dengan normal. Kalau terminal ke-close paksa atau proses crash di tengah kerja, riwayat query semalam tetap ada besok paginya.

Kenapa Rust dan hexagonal architecture

Saya pilih Rust karena binary tunggal tanpa runtime, cepat, dan cocok untuk tool yang dipakai ratusan kali sehari. Tapi keputusan yang lebih menentukan adalah arsitekturnya.

pgrs dibangun sebagai hexagonal architecture (ports & adapters), dipecah jadi dua crate: pgrs-core menyimpan seluruh domain, service, dan port (trait) — sedangkan pgrs-cli cuma berisi parsing argumen, REPL, dan tampilan. pgrs-cli hanya boleh bicara lewat facade publik pgrs-core (ConnectionApi, QueryApi, SchemaApi, dst); modul services/ports/adapters di dalam core bersifat pub(crate) dan tidak bisa dijangkau dari luar — batas ini ditegakkan oleh compiler, bukan sekadar konvensi.

Untuk proyek sekecil ini, pemisahan itu mungkin terdengar berlebihan. Tapi justru itu poinnya: setiap kali saya menambah fitur besar — REPL, autocomplete, \explain, transaction guard — saya merasakan sendiri kapan batas antar-layer itu menyelamatkan waktu. Logika SQL (tokenizer, alias-map, deteksi DDL/DML) bisa dites tanpa koneksi Postgres sungguhan, dan kalau suatu hari saya bikin pgrs-desktop atau pgrs-web, semua logika itu tinggal dipakai ulang tanpa ditulis dari nol.

Yang saya pelajari

Membangun pgrs mengajari saya beberapa hal yang tidak saya duga di awal:

Coba sendiri

pgrs open source, untuk Linux dan macOS. Satu baris ini sudah cukup untuk install:

BASH
curl -fsSL https://raw.githubusercontent.com/fakhrulnugroho/pgrs/main/install.sh | bash

Kode dan rilis terbarunya ada di github.com/fakhrulnugroho/pgrs (v0.9.0 adalah rilis terbaru saat tulisan ini dibuat). Kalau kamu juga sering kesal dengan hal-hal receh di workflow harian, coba jadikan itu satu project kecil — kadang itu yang paling menyenangkan untuk dibangun, karena kamu sendiri adalah user pertamanya.