Menata lapisan penyimpanan Backstage: perbandingan SQLite sebagai default pengembangan dengan PostgreSQL untuk produksi, peran Knex dan migrasi database, connection pooling dengan PgBouncer, serta konfigurasi cache Redis dan search index store untuk skala besar.

Di episode 11, kalian memperdalam catalog advanced: entity relations, ownership, catalog filters, hingga menjaga kualitas entity dengan validator di CI. Episode 12 ini bergeser dari bentuk data ke tempat penyimpanannya. Hampir semua plugin Backstage — catalog, scaffolder, auth, search — menulis ke database, dan begitu instance dipakai banyak orang, lapisan database justru menjadi titik yang paling sering menjadi sumber masalah. Memahami lapisan ini akan menyelamatkan kalian dari downtime yang tidak perlu.
Backstage tidak mengunci satu jenis database tunggal. Setiap plugin backend memilih store yang sesuai dengan datanya:
Ketiga lapisan ini dikonfigurasi lewat app-config.yaml, masing-masing di bawah key database, cache, dan search.
Saat baru melakukan bootstrap lewat npx @backstage/create-app@latest, kalian menemukan bahwa instance berjalan tanpa setup database tambahan. Itu karena Backstage menggunakan SQLite sebagai store default — zero-configuration, tersimpan sebagai file, dan sempurna untuk eksperimen di laptop.
backend:
database:
client: better-sqlite3
connection: ':memory:'Kelebihan SQLite: tidak perlu server terpisah, portabel, dan cukup untuk development single-user. Kelemahannya jelas: tidak dirancang untuk beban bersamaan (concurrent writes), berbentuk single-file, dan tidak mendukung koneksi dari banyak instance sekaligus. Karena itu SQLite hanya layak untuk development dan test, bukan produksi.
Saat kalian meluncurkan Backstage untuk tim sungguhan, PostgreSQL menjadi pilihan standar. Backstage mendukung Postgres sebagai database shared yang bisa diakses oleh banyak instance backend sekaligus — syarat mutlak untuk horizontal scaling.
backend:
database:
client: pg
connection:
host: postgres.internal
port: 5432
user: backstage
password: ${POSTGRES_PASSWORD}
database: backstagePerhatikan client: pg — itu memberi tahu Backstage bahwa koneksinya memakai driver PostgreSQL. Di belakang layar, koneksi ini dibuat lewat Knex, query builder yang dipakai semua plugin database Backstage.
| Aspek | SQLite | PostgreSQL |
|---|---|---|
| Setup | Zero-config, file lokal | Butuh server dan kredensial |
| Concurrency | Terbatas, satu penulis | Banyak pembaca dan penulis |
| Multi-instance | Tidak cocok | Mendukung shared database |
| Migrasi | Didukung Knex | Didukung Knex |
| Penggunaan | Development dan test | Produksi |
Aturan praktisnya sederhana: mulai dengan SQLite agar cepat bereksperimen, lalu pindah ke PostgreSQL segera setelah environment produksi atau preview diinginkan. Banyak tim justru memakai Postgres sejak awal development untuk menghindari kejutan "berbeda antara laptop dan produksi".
Tip
Pindahlah ke PostgreSQL lebih awal daripada nanti. Semakin lama kalian menumpuk data di SQLite, semakin besar usaha migrasi di kemudian hari. Postgres lokal lewat Docker cukup untuk menyamakan perilaku dengan produksi.
Knex adalah abstraksi database yang dipakai Backstage di hampir semua plugin. Dengan Knex, satu kode query bisa berjalan di SQLite maupun PostgreSQL tanpa diubah. Yang lebih penting, Knex menyediakan migrasi — skema database dikelola sebagai file migrasi yang versinya tercatat di tabel khusus. Lapisan database ini dibungkus menjadi service yang bisa diimpor dari @backstage/backend-defaults.
Saat kalian mengubah skema plugin atau menambahkan plugin baru, migrasi perlu dijalankan agar skema database sinkron:
yarn backstage-cli package schema migrate --package @internal/plugin-catalogPerintah ini memastikan tabel yang dibutuhkan sebuah paket ada dan versinya sesuai. Dalam deployment production, migrasi biasanya dijalankan sebagai langkah sebelum aplikasi di-start — jangan pernah menaruh migrasi di dalam logika runtime yang hanya berjalan sekali.
PostgreSQL membatasi jumlah koneksi aktif per instance. Masalahnya, Backstage membuka beberapa koneksi per plugin backend, dan jika kalian menjalankan banyak instance, jumlah koneksi bisa meledak. Di sinilah connection pooling berperan: pooler meminjamkan koneksi dari kumpulan kecil dan mengembalikannya setelah selesai.
PgBouncer adalah pooler paling umum dipakai bersama Backstage:
[databases]
backstage = host=postgres.internal port=5432 dbname=backstage
[pgbouncer]
listen_port = 6432
pool_mode = transaction
max_client_conn = 1000Dengan pool_mode = transaction, koneksi dipegang hanya selama satu transaksi berjalan, lalu dilepas kembali ke pool — pola yang cocok dengan workload Backstage yang pendek-pendek. Aplikasi lalu diarahkan ke port 6432 alih-alih langsung ke Postgres di 5432.
Sebagian data Backstage tidak butuh database relasional — cukup cache cepat yang bisa dibagikan antar instance. Default-nya adalah in-memory, yang bekerja untuk instance tunggal tetapi tidak konsisten begitu ada lebih dari satu instance. Untuk skala besar, Backstage mendukung Redis sebagai store cache:
cache:
backend:
store: redis
connection:
url: redis://redis.internal:6379Dengan Redis, cache bisa dibagikan antar instance sehingga hasil yang di-cache instance A juga berguna bagi instance B. Pastikan Redis dipasang di environment terpisah dari Postgres agar beban keduanya tidak saling mengganggu.
Fitur Search (yang akan kalian dalami di episode 16) membutuhkan index store. Secara default, index search disimpan di database utama — cukup untuk tim kecil. Saat jumlah entity dan dokumen membesar, kalian bisa mengalihkan search index ke engine khusus seperti Elasticsearch agar query tetap cepat tanpa membebani database utama.
Konfigurasi search engine dilakukan lewat key search di app-config.yaml. Keputusan besarnya: mulai dengan index di database, lalu pindahkan ke engine dedicated ketika volume dokumen dan kebutuhan query sudah melebihi kemampuan database utama.
Episode 12 ini menata pondasi data Backstage: SQLite untuk development dan PostgreSQL untuk produksi, peran Knex dan migrasi untuk menjaga skema tetap konsisten, PgBouncer untuk memitigasi ledakan koneksi, Redis untuk cache yang bisa dibagikan antar instance, serta arah untuk memisahkan search index di skala besar.
Inti yang harus dibawa pulang:
Di episode 13 berikutnya, kalian masuk ke authorization: permission framework dan RBAC — bagaimana mengontrol siapa yang boleh melihat entity, menjalankan template, dan melakukan aksi tertentu. Data yang tersimpan rapi di episode ini akan dijaga dengan aturan akses yang tegas.