1. Pengenalan CouchDB
Apache CouchDB adalah database NoSQL berbasis dokumen yang menyimpan data dalam format JSON. Dikembangkan oleh Apache Software Foundation, CouchDB terkenal dengan fitur multi-master replication — memungkinkan sinkronisasi data antar server bahkan dalam kondisi offline.
Karakteristik Utama CouchDB
| Fitur | Deskripsi |
|---|---|
| Document-Oriented | Data disimpan sebagai JSON document dengan field yang fleksibel |
| Multi-Master Replication | Setiap node bisa menerima write, sync satu sama lain |
| Offline-First | Bekerja tanpa koneksi internet, sync saat online kembali |
| RESTful API | Semua operasi melalui HTTP — GET, PUT, POST, DELETE |
| Eventual Consistency | Data akhirnya konsisten di semua node, tidak instant |
| Built-in Conflict Resolution | Mendeteksi dan menangani konflik saat replikasi |
| Fauxton Web UI | Admin dashboard bawaan di browser |
CouchDB vs Database Lain
| Aspek | CouchDB | MongoDB | PostgreSQL |
|---|---|---|---|
| Tipe | Document NoSQL | Document NoSQL | Relational SQL |
| Format | JSON | BSON (binary JSON) | Rows & Columns |
| API | RESTful HTTP | Wire Protocol | SQL via TCP |
| Replication | Multi-master | Replica Set (primary-secondary) | Primary-replica |
| Query | Mango / MapReduce Views | MQL (Mongo Query Language) | SQL |
| Schema | Schema-less | Schema-less | Schema-enforced |
| Best For | Offline-first, mobile, sync | General purpose NoSQL | Complex queries, ACID |
+--------------------------------------------------------------------+
| ARSITEKTUR COUCHDB |
| |
| +------------------+ +------------------+ +---------------+ |
| | Server A | | Server B | | Mobile App | |
| | +------------+ | | +------------+ | | +----------+ | |
| | | Database | |<-->| | Database | |<-->| | PouchDB | | |
| | | (shard 1) | | | | (shard 2) | | | | (local) | | |
| | +------------+ | | +------------+ | | +----------+ | |
| | | Views | | | | Views | | | | |
| | +------------+ | | +------------+ | | Replication | |
| +------------------+ +------------------+ +---------------+ |
| |
| API: HTTP REST |
| GET /database/document_id |
| PUT /database/document_id { JSON body } |
+--------------------------------------------------------------------+
2. Instalasi & Setup
Instalasi CouchDB
# =============================================
# Docker (recommended untuk development)
# =============================================
docker run -d --name couchdb \
-e COUCHDB_USER=admin \
-e COUCHDB_PASSWORD=password123 \
-p 5984:5984 \
apache/couchdb:3.3
# =============================================
# Ubuntu/Debian
# =============================================
# Tambah repository
curl -L https://couchdb.apache.org/repo/keys.asc | sudo apt-key add -
echo "deb https://apache.jfrog.io/artifactory/couchdb-deb/ jammy main" | \
sudo tee /etc/apt/sources.list.d/couchdb.list
sudo apt update
sudo apt install couchdb
# =============================================
# macOS
# =============================================
brew install couchdb
brew services start couchdb
# =============================================
# Verifikasi instalasi
# =============================================
curl http://localhost:5984/
# Output: {"couchdb":"Welcome","version":"3.3.3",...}
# Akses Fauxton (Web UI):
# http://localhost:5984/_utils/
Konfigurasi Awal
# Setup single-node cluster
curl -X POST http://localhost:5984/_cluster_setup \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{"action":"enable_single_node","username":"admin","password":"password123","bind_address":"0.0.0.0","port":5984}'
# Buat database baru
curl -X PUT http://localhost:5984/toko_online \
-u admin:password123
# Lihat semua database
curl http://localhost:5984/_all_dbs \
-u admin:password123
# Hapus database
curl -X DELETE http://localhost:5984/toko_online \
-u admin:password123
Node.js: nano Library
// Instalasi nano (CouchDB client untuk Node.js)
// npm install nano
const Nano = require('nano');
const nano = Nano('http://admin:***@localhost:5984');
async function main() {
// Buat database
try {
await nano.db.create('toko_online');
console.log('Database berhasil dibuat');
} catch (err) {
if (err.statusCode === 412) {
console.log('Database sudah ada');
}
}
// Gunakan database
const db = nano.use('toko_online');
// Insert document
const response = await db.insert({
nama: 'Budi Santoso',
email: 'budi@email.com',
kota: 'Jakarta',
is_active: true,
tanggal_daftar: new Date().toISOString()
});
console.log('Document ID:', response.id);
console.log('Revision:', response.rev);
}
main().catch(console.error);
3. Documents & JSON
Di CouchDB, semua data disimpan sebagai document dalam format JSON. Setiap document punya _id (unik) dan _rev (revision) yang di-manage otomatis oleh CouchDB.
Struktur Document
{
"_id": "pelanggan_001",
"_rev": "3-1a2b3c4d5e6f",
"tipe": "pelanggan",
"nama": "Budi Santoso",
"email": "budi@email.com",
"telepon": "08123456789",
"alamat": {
"jalan": "Jl. Sudirman No. 123",
"kota": "Jakarta",
"provinsi": "DKI Jakarta",
"kode_pos": "12190"
},
"pesanan": [
{
"id_produk": "prod_001",
"nama_produk": "Laptop ASUS",
"harga": 12000000,
"jumlah": 1
},
{
"id_produk": "prod_002",
"nama_produk": "Mouse Logitech",
"harga": 250000,
"jumlah": 2
}
],
"is_active": true,
"tags": ["premium", "jakarta"],
"created_at": "2026-06-26T10:00:00Z"
}
Field Khusus CouchDB
| Field | Deskripsi | Contoh |
|---|---|---|
_id | ID unik document (bisa di-set manual atau auto-generate) | "pelanggan_001" |
_rev | Revision — di-update setiap perubahan | "3-1a2b3c4d" |
_deleted | Flag soft delete | true |
_attachments | File binary yang di-attach ke document | { "foto.jpg": {...} } |
Untuk mengelola struktur document, gunakan field tipe (atau type) sebagai discriminator. CouchDB tidak punya "table" seperti RDBMS — semua document di satu database, di-filter berdasarkan tipe.
4. CRUD Operations
Semua operasi di CouchDB melalui HTTP REST API. Berikut panduan lengkap CRUD:
Create — Membuat Document
# POST: Auto-generate _id
curl -X POST http://localhost:5984/toko_online \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"tipe": "pelanggan",
"nama": "Sari Dewi",
"email": "sari@email.com",
"kota": "Bandung",
"is_active": true
}'
# Response: {"ok":true,"id":"abc123...","rev":"1-def456..."}
# PUT: Tentukan _id sendiri
curl -X PUT http://localhost:5984/toko_online/pelanggan_001 \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"tipe": "pelanggan",
"nama": "Budi Santoso",
"email": "budi@email.com",
"kota": "Jakarta",
"is_active": true
}'
Read — Membaca Document
# GET: Ambil document berdasarkan ID
curl http://localhost:5984/toko_online/pelanggan_001 \
-u admin:password123
# GET: Ambil dengan revisi tertentu
curl "http://localhost:5984/toko_online/pelanggan_001?rev=2-abc123" \
-u admin:password123
# HEAD: Cek apakah document ada
curl -I http://localhost:5984/toko_online/pelanggan_001 \
-u admin:password123
# GET: Ambil beberapa document sekaligus
curl -X POST http://localhost:5984/toko_online/_all_docs \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{"include_docs": true, "keys": ["pelanggan_001", "pelanggan_002"]}'
# GET: Semua document (dengan pagination)
curl "http://localhost:5984/toko_online/_all_docs?include_docs=true&limit=10" \
-u admin:password123
Update — Memperbarui Document
# PUT: Update document (WAJIB menyertakan _rev terbaru)
curl -X PUT http://localhost:5984/toko_online/pelanggan_001 \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"_rev": "2-abc123def456",
"tipe": "pelanggan",
"nama": "Budi Santoso",
"email": "budi_baru@email.com",
"kota": "Surabaya",
"is_active": true
}'
# Response: {"ok":true,"id":"pelanggan_001","rev":"3-789ghi..."}
Delete — Menghapus Document
# DELETE: Hapus document (WAJIB _rev) curl -X DELETE "http://localhost:5984/toko_online/pelanggan_001?rev=3-789ghi" \ -u admin:password123 # CouchDB melakukan soft delete — document ditandai _deleted:true # Data tetap ada di database sampai compaction
CRUD dengan Node.js (nano)
const Nano = require('nano');
const nano = Nano('http://admin:***@localhost:5984');
const db = nano.use('toko_online');
async function crudDemo() {
// CREATE
const createResult = await db.insert({
tipe: 'pelanggan',
nama: 'Rina Wati',
email: 'rina@email.com',
kota: 'Yogyakarta'
});
const docId = createResult.id;
// READ
const doc = await db.get(docId);
console.log('Nama:', doc.nama);
// UPDATE (wajib sertakan _rev)
doc.email = 'rina_baru@email.com';
doc.kota = 'Semarang';
const updateResult = await db.insert(doc);
console.log('New revision:', updateResult.rev);
// DELETE
await db.destroy(docId, updateResult.rev);
console.log('Document deleted');
// BATCH INSERT
const batchDocs = [
{ tipe: 'produk', nama_produk: 'Laptop', harga: 12000000 },
{ tipe: 'produk', nama_produk: 'Mouse', harga: 250000 },
{ tipe: 'produk', nama_produk: 'Keyboard', harga: 500000 }
];
const batchResult = await db.bulk({ docs: batchDocs });
console.log('Batch inserted:', batchDocs.length, 'documents');
}
crudDemo().catch(console.error);
5. Replication & Sync
Fitur terkuat CouchDB adalah multi-master replication. Setiap node bisa menerima write dan menyinkronkan perubahan ke node lain. Ini mendukung skenario offline-first dan distributed.
Cara Kerja Replication
+--------------------------------------------------------------------+ | MULTI-MASTER REPLICATION | | | | Server A (Jakarta) Server B (Bandung) | | +-------------------+ +-------------------+ | | | DB: toko_online |<---->| DB: toko_online | | | | Write: OK | | Write: OK | | | | Read: OK | | Read: OK | | | +-------------------+ +-------------------+ | | ^ ^ | | | | | | Mobile App (Offline) Tablet (Offline) | | +-------------------+ +-------------------+ | | | PouchDB (local) | | PouchDB (local) | | | | Write: OK | | Write: OK | | | | Sync saat online | | Sync saat online | | | +-------------------+ +-------------------+ | | | | Semua node bisa WRITE. Sync terjadi otomatis. | | Conflict di-handle berdasarkan revision tree. | +--------------------------------------------------------------------+
Menjalankan Replication
# Replication satu arah: Server A -> Server B
curl -X POST http://localhost:5984/_replicate \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"source": "http://localhost:5984/toko_online",
"target": "http://server-b:5984/toko_online",
"continuous": false
}'
# Replication dua arah (bi-directional)
curl -X POST http://localhost:5984/_replicate \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"source": "http://localhost:5984/toko_online",
"target": "http://server-b:5984/toko_online",
"continuous": true,
"create_target": true
}'
# Filtered replication: hanya sync data tertentu
curl -X POST http://localhost:5984/_replicate \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"source": "http://localhost:5984/toko_online",
"target": "http://server-b:5984/toko_online",
"continuous": true,
"filter": "app/hanya_pelanggan_aktif"
}'
Replication Filter
// Design Document dengan filter function
// Simpan sebagai _design/app di database
{
"_id": "_design/app",
"filters": {
"hanya_pelanggan_aktif": "function(doc, req) {
return doc.tipe === 'pelanggan' && doc.is_active === true;
}",
"hanya_produk": "function(doc, req) {
return doc.tipe === 'produk';
}"
}
}
// Menggunakan filter dengan Node.js nano
const Nano = require('nano');
const nano = Nano('http://admin:***@localhost:5984');
const db = nano.use('toko_online');
// Simpan design document
await db.insert({
_id: '_design/app',
filters: {
hanya_pelanggan_aktif: function(doc, req) {
return doc.tipe === 'pelanggan' && doc.is_active === true;
}.toString()
}
});
// Jalankan filtered replication
const replicator = nano.db.use('_replicator');
await replicator.insert({
_id: 'sync_ke_mobile',
source: 'http://localhost:5984/toko_online',
target: 'http://mobile-server:5984/toko_online',
continuous: true,
filter: 'app/hanya_pelanggan_aktif'
});
PouchDB: CouchDB di Browser/Mobile
// PouchDB: CouchDB di browser/mobile
// npm install pouchdb
const PouchDB = require('pouchdb');
const localDB = new PouchDB('toko_online');
const remoteDB = new PouchDB('http://admin:***@localhost:5984/toko_online');
// Simpan data secara lokal (bisa offline)
await localDB.put({
_id: 'pelanggan_001',
nama: 'Budi Santoso',
email: 'budi@email.com'
});
// Sync dengan server (otomatis reconcile)
localDB.sync(remoteDB, {
live: true, // Continuous sync
retry: true // Retry jika gagal
}).on('change', function(info) {
console.log('Sync change:', info.direction);
}).on('error', function(err) {
console.error('Sync error:', err);
});
6. Views & MapReduce
Views adalah mekanisme query utama di CouchDB. Menggunakan paradigma MapReduce untuk mengindeks dan meng-aggregate data.
MapReduce Concepts
| Komponen | Fungsi | Analogi SQL |
|---|---|---|
| Map Function | Untuk setiap document, emit key-value pairs | SELECT ... WHERE ... |
| Reduce Function | Aggregate hasil map (count, sum, stats) | GROUP BY + SUM/COUNT |
| View | Kombinasi Map + Reduce, diindeks otomatis | Materialized View |
// Design Document dengan Views
// Simpan sebagai _design/toko di database
{
"_id": "_design/toko",
"views": {
"pelanggan_per_kota": {
"map": "function(doc) {
if (doc.tipe === 'pelanggan' && doc.is_active) {
emit(doc.kota, doc.nama);
}
}"
},
"jumlah_pelanggan_per_kota": {
"map": "function(doc) {
if (doc.tipe === 'pelanggan' && doc.is_active) {
emit(doc.kota, 1);
}
}",
"reduce": "_count"
},
"total_belanja_per_pelanggan": {
"map": "function(doc) {
if (doc.tipe === 'pesanan' && doc.status !== 'batal') {
emit(doc.id_pelanggan, doc.total);
}
}",
"reduce": "_sum"
},
"produk_per_kategori": {
"map": "function(doc) {
if (doc.tipe === 'produk') {
emit([doc.kategori, doc.harga], doc.nama_produk);
}
}"
}
}
}
Query Views
# Query view: pelanggan per kota curl "http://localhost:5984/toko_online/_design/toko/_view/pelanggan_per_kota" \ -u admin:password123 # Filter berdasarkan key curl "http://localhost:5984/toko_online/_design/toko/_view/pelanggan_per_kota?key=%22Jakarta%22" \ -u admin:password123 # Range query curl "http://localhost:5984/toko_online/_design/toko/_view/pelanggan_per_kota?startkey=%22A%22&endkey=%22M%22" \ -u admin:password123 # Query reduce: jumlah pelanggan per kota curl "http://localhost:5984/toko_online/_design/toko/_view/jumlah_pelanggan_per_kota?group=true" \ -u admin:password123 # Total belanja per pelanggan curl "http://localhost:5984/toko_online/_design/toko/_view/total_belanja_per_pelanggan?group=true" \ -u admin:password123 # Include documents (join-like) curl "http://localhost:5984/toko_online/_design/toko/_view/pelanggan_per_kota?include_docs=true&limit=10" \ -u admin:password123
7. Mango Queries (Find)
Mango Query (disebut juga _find) adalah cara query yang lebih intuitif daripada MapReduce Views. Mirip dengan MongoDB query syntax.
# Cari semua pelanggan di Jakarta
curl -X POST http://localhost:5984/toko_online/_find \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"selector": {
"tipe": "pelanggan",
"kota": "Jakarta",
"is_active": true
},
"fields": ["_id", "nama", "email", "kota"],
"sort": [{"nama": "asc"}],
"limit": 20
}'
# Query dengan operator: $gt, $lt, $in, $regex
curl -X POST http://localhost:5984/toko_online/_find \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"selector": {
"tipe": "produk",
"$and": [
{"harga": {"$gte": 100000}},
{"harga": {"$lte": 5000000}},
{"stok": {"$gt": 0}}
],
"kategori": {"$in": ["Elektronik", "Fashion"]}
},
"sort": [{"harga": "asc"}],
"limit": 50
}'
# Text search dengan $regex
curl -X POST http://localhost:5984/toko_online/_find \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"selector": {
"tipe": "pelanggan",
"nama": {"$regex": "(?i)budi"}
}
}'
Mango Index
# Buat index untuk performa query
curl -X POST http://localhost:5984/toko_online/_index \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"index": {
"fields": ["tipe", "kota", "is_active"]
},
"name": "idx_pelanggan_kota",
"type": "json"
}'
# Compound index untuk sorting
curl -X POST http://localhost:5984/toko_online/_index \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"index": {
"fields": ["tipe", "kategori", "harga"]
},
"name": "idx_produk_kategori_harga",
"type": "json"
}'
# Lihat semua index
curl http://localhost:5984/toko_online/_index \
-u admin:password123
# Explain query (lihat execution plan)
curl -X POST http://localhost:5984/toko_online/_explain \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{
"selector": {
"tipe": "pelanggan",
"kota": "Jakarta"
}
}'
8. Clustering & Sharding
CouchDB 3.x mendukung clustering built-in — data di-shard ke beberapa node dan setiap shard di-replicate ke beberapa node untuk redundansi.
Konsep Clustering
| Konsep | Deskripsi | Default |
|---|---|---|
| Shards (Q) | Jumlah partisi database | 8 |
| Replicas (N) | Jumlah salinan setiap shard | 3 |
| Write Quorum (W) | Node yang harus konfirmasi write | 2 |
| Read Quorum (R) | Node yang harus setuju saat read | 2 |
# =============================================
# 3-Node Cluster Setup
# =============================================
# Node 1 (seed)
docker run -d --name couch1 -e COUCHDB_USER=admin \
-e COUCHDB_PASSWORD=password123 \
--net=couchnet --net-alias=couch1 \
-p 5984:5984 apache/couchdb:3.3
# Node 2
docker run -d --name couch2 -e COUCHDB_USER=admin \
-e COUCHDB_PASSWORD=password123 \
--net=couchnet --net-alias=couch2 \
-p 5985:5984 apache/couchdb:3.3
# Node 3
docker run -d --name couch3 -e COUCHDB_USER=admin \
-e COUCHDB_PASSWORD=password123 \
--net=couchnet --net-alias=couch3 \
-p 5986:5984 apache/couchdb:3.3
# Join node 2 ke cluster
curl -X POST http://localhost:5984/_cluster_setup \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{"action":"enable_cluster","username":"admin","password":"password123","bind_address":"0.0.0.0","port":5984}'
curl -X POST http://localhost:5984/_cluster_setup \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{"action":"add_node","username":"admin","password":"password123","host":"couch2","port":5984}'
# Finish cluster setup
curl -X POST http://localhost:5984/_cluster_setup \
-H "Content-Type: application/json" \
-u admin:password123 \
-d '{"action":"finish_cluster"}'
# Cek cluster membership
curl http://localhost:5984/_membership \
-u admin:password123
9. Use Cases & Best Practices
Ideal Use Cases untuk CouchDB
| Use Case | Mengapa CouchDB Cocok |
|---|---|
| Mobile Apps (offline-first) | PouchDB di device + sync saat online |
| CRM / Sales Tools | Sales bisa input data di lapangan tanpa internet |
| Content Management | Document model cocok untuk artikel, halaman |
| IoT Data Collection | Device mengumpulkan data offline, sync ke server |
| Collaborative Apps | Multi-user edit dengan conflict resolution |
| Distributed Systems | Multi-master di beberapa lokasi geografis |
Best Practices
| Praktik | Penjelasan |
|---|---|
| Gunakan prefix untuk _id | Misal "pelanggan_001" agar mudah di-identify |
| Buat index untuk Mango queries | Tanpa index, Mango query scan semua documents |
| Design documents di version control | Views adalah bagian dari kode, bukan database manual |
| Compact database secara berkala | CouchDB menyimpan semua revisi, perlu di-compact |
| Gunakan filtered replication | Jangan sync semua data ke mobile, hanya yang relevan |
| Handle conflict di aplikasi | CouchDB mendeteksi conflict, tapi resolusi di sisi app |
# Compact database (hapus revisi lama) curl -X POST http://localhost:5984/toko_online/_compact \ -H "Content-Type: application/json" \ -u admin:password123 # Compact design documents curl -X POST http://localhost:5984/toko_online/_compact/app \ -H "Content-Type: application/json" \ -u admin:password123 # View cleanup (hapus view indexes yang tidak dipakai) curl -X POST http://localhost:5984/toko_online/_view_cleanup \ -u admin:password123 # Cek database info curl http://localhost:5984/toko_online \ -u admin:password123 # Active tasks (replication, compaction, indexing) curl http://localhost:5984/_active_tasks \ -u admin:password123
10. Quiz Pemahaman
- Apa fungsi field _rev pada document CouchDB?
Jawaban: Revision tracking — setiap perubahan document menghasilkan _rev baru. Diperlukan untuk update/delete dan untuk mendeteksi conflict saat replication. - Apa perbedaan Views dan Mango Queries?
Jawaban: Views menggunakan MapReduce (JavaScript functions), hasilnya diindeks. Mango Queries mirip MongoDB syntax (_find), lebih mudah tapi perlu index terpisah. - Apa keunggulan multi-master replication?
Jawaban: Setiap node bisa menerima write — cocok untuk distributed/offline-first apps. Tidak ada single point of failure seperti primary-replica. - Bagaimana CouchDB menangani conflict?
Jawaban: CouchDB menyimpan semua revisi dalam revision tree. Saat conflict (dua perubahan pada revisi yang sama), app harus memilih versi mana yang valid. - Kapan sebaiknya TIDAK menggunakan CouchDB?
Jawaban: Untuk aplikasi yang butuh complex JOIN, transaksi ACID yang ketat, atau analitik relational. CouchDB lebih cocok untuk document-centric dan distributed apps.
Pelajari TimescaleDB untuk time-series data di PostgreSQL, atau Database Sharding untuk strategi skalabilitas horizontal pada database relasional.