Apache CouchDB Guide — Document-Oriented NoSQL Database
Apache CouchDB is a document-oriented NoSQL database that stores JSON documents, exposes a RESTful HTTP API, and provides multi-master replication, conflict resolution, and a built-in MapReduce view engine.
What You’ll Learn
By the end of this tutorial, you’ll understand CouchDB’s document model, interact with the database using the REST API, create MapReduce views for querying, set up replication between instances, resolve conflicts, and synchronize with PouchDB for offline-first web applications.
Why CouchDB Matters
CouchDB is uniquely designed for offline-first and multi-device sync scenarios. It powers applications that need to work without internet — like mobile field service apps, point-of-sale systems in remote areas, and collaborative tools. Doda Browser uses CouchDB for its cross-device bookmark sync, while Durga Antivirus Pro leverages CouchDB’s replication for distributing threat definitions to offline endpoints. Learning CouchDB gives you a skill essential for modern offline-capable applications.
CouchDB Learning Path
flowchart LR
A[SQL Basics] --> B[MongoDB]
B --> C[CouchDB]
C --> D[Elasticsearch]
D --> E[Redis]
E --> F[Database Design]
C --> G{You Are Here}
style G fill:#f90,color:#fff
What Is CouchDB? (The “Why” First)
Think of CouchDB as a database that speaks HTTP. Unlike SQL databases that use custom wire protocols, every CouchDB operation is an HTTP request. Your data is JSON, your queries are HTTP GET requests, your updates are HTTP PUT/POST requests. This makes CouchDB incredibly easy to use from any web or mobile application — no special drivers needed. But CouchDB’s superpower is replication: you can have CouchDB instances on phones, laptops, and servers, all syncing data automatically, even with intermittent connectivity.
CouchDB vs MongoDB
| Feature | CouchDB | MongoDB |
|---|---|---|
| Storage | Append-only (no overwrite) | In-place updates |
| Query | MapReduce views | Aggregation pipeline, indexes |
| API | RESTful HTTP | Custom wire protocol (BSON) |
| Replication | Multi-master (built-in) | Replica sets (master-slave) |
| Conflict handling | Automatic (multi-victor) | Last-write-wins |
| ACID | Per-document | Multi-document |
| Best for | Offline-first, sync apps | General-purpose, analytics |
Documents — The Core of CouchDB
Everything in CouchDB is a JSON document. Each document has a unique _id and a revision _rev (used for conflict detection):
{
"_id": "user_alice",
"_rev": "1-abc123def456",
"type": "user",
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28,
"addresses": [
{"city": "New York", "zip": "10001", "type": "home"},
{"city": "Boston", "zip": "02101", "type": "work"}
],
"created_at": "2026-06-07T10:00:00Z"
}Working with the REST API
All CouchDB interactions use HTTP methods:
# Create a database
curl -X PUT http://admin:password@localhost:5984/shop
# Response: {"ok":true}
# Create a document
curl -X POST http://admin:password@localhost:5984/shop \
-H "Content-Type: application/json" \
-d '{
"_id": "product_laptop",
"type": "product",
"name": "MacBook Pro",
"price": 2499,
"category": "Electronics",
"in_stock": true,
"tags": ["apple", "laptop", "pro"]
}'
# Response: {"ok":true, "id":"product_laptop", "rev":"1-abc123"}
# Create another document
curl -X POST http://admin:password@localhost:5984/shop \
-H "Content-Type: application/json" \
-d '{
"_id": "product_mouse",
"type": "product",
"name": "Magic Mouse",
"price": 79,
"category": "Electronics",
"in_stock": true,
"tags": ["apple", "mouse", "wireless"]
}'Reading Documents
# Get a document by ID
curl http://admin:password@localhost:5984/shop/product_laptop
# Response:
{
"_id": "product_laptop",
"_rev": "1-abc123",
"type": "product",
"name": "MacBook Pro",
"price": 2499,
"category": "Electronics",
"in_stock": true,
"tags": ["apple", "laptop", "pro"]
}
# Update a document (include _rev!)
curl -X PUT http://admin:password@localhost:5984/shop/product_laptop \
-H "Content-Type: application/json" \
-d '{
"_id": "product_laptop",
"_rev": "1-abc123",
"name": "MacBook Pro 2026",
"price": 2599,
"category": "Electronics",
"in_stock": true,
"tags": ["apple", "laptop", "pro"]
}'
# Response: {"ok":true, "id":"product_laptop", "rev":"2-def456"}MapReduce Views — Querying Data
Unlike MongoDB’s aggregation pipeline, CouchDB uses MapReduce views — pre-computed indexes defined as JavaScript functions:
// View: Products by category
// Saved as _design/products/_views/by_category
// Map function
function(doc) {
if (doc.type === 'product' && doc.category) {
emit(doc.category, {
name: doc.name,
price: doc.price,
in_stock: doc.in_stock
});
}
}
// Reduce function (optional) — count per category
function(keys, values, rereduce) {
return values.length;
}Query the view:
# Get all products grouped by category
curl http://admin:password@localhost:5984/shop/_design/products/_view/by_category?group=true
# Response:
{
"rows": [
{"key": "Electronics", "value": 2}
]
}
# Get products in Electronics category
curl http://admin:password@localhost:5984/shop/_design/products/_view/by_category?key="Electronics"
# Response:
{
"rows": [
{"key": "Electronics", "value": {"name": "MacBook Pro", "price": 2499, "in_stock": true}},
{"key": "Electronics", "value": {"name": "Magic Mouse", "price": 79, "in_stock": true}}
]
}More Complex View — Products by Price Range
// Map function
function(doc) {
if (doc.type === 'product' && doc.price) {
var range;
if (doc.price < 100) range = 'budget';
else if (doc.price < 500) range = 'mid';
else range = 'premium';
emit([range, doc.price], doc.name);
}
}# Query premium products
curl 'http://admin:password@localhost:5984/shop/_design/products/_view/by_price_range?startkey=["premium",0]&endkey=["premium",999999]'
# Response:
{
"rows": [
{"key": ["premium", 2499], "value": "MacBook Pro"}
]
}Replication
CouchDB’s replication is its killer feature — it syncs databases between any two endpoints:
# Trigger replication from a remote database to local
curl -X POST http://admin:password@localhost:5984/_replicate \
-H "Content-Type: application/json" \
-d '{
"source": "http://admin:password@remote-server:5984/shop",
"target": "shop_local",
"continuous": true
}'
# One-shot replication
curl -X POST http://admin:password@localhost:5984/_replicate \
-H "Content-Type: application/json" \
-d '{
"source": "shop",
"target": "http://admin:password@backup-server:5984/shop_backup"
}'CouchDB Replication Architecture
flowchart TB
subgraph Server
C1[CouchDB Main]
S1[_replicator Database]
end
subgraph Mobile Devices
P1[PouchDB - Phone]
P2[PouchDB - Tablet]
P3[PouchDB - Laptop]
end
subgraph Backup
C2[CouchDB Backup]
end
C1 <-->|Continuous Replication| P1
C1 <-->|Continuous Replication| P2
C1 <-->|Continuous Replication| P3
C1 -->|One-shot Replication| C2
P1 <-->|Peer-to-Peer| P2
Every CouchDB (and PouchDB) instance can replicate with every other instance. Changes flow in both directions — multi-master replication built in.
Conflict Resolution
When the same document is edited on two offline devices, CouchDB creates conflicting revisions:
# Check for conflicts
curl http://admin:password@localhost:5984/shop/product_laptop?conflicts=true
# Response shows conflicting revisions:
{
"_id": "product_laptop",
"_rev": "3-ghi789",
"name": "MacBook Pro",
...
"_conflicts": ["2-def456"]
}
# Get the conflicting revision
curl http://admin:password@localhost:5984/shop/product_laptop?rev=2-def456
# Resolve by merging and deleting the conflict
curl -X PUT http://admin:password@localhost:5984/shop/product_laptop \
-H "Content-Type: application/json" \
-d '{
"_id": "product_laptop",
"_rev": "3-ghi789",
"name": "MacBook Pro (merged)",
...
}'
# Delete the conflict revision
curl -X DELETE http://admin:password@localhost:5984/shop/product_laptop?rev=2-def456PouchDB — CouchDB for the Browser
PouchDB is a JavaScript implementation of CouchDB that runs in the browser:
// Create a local database
const db = new PouchDB('shop_offline');
// Save a document
db.put({
_id: 'product_tablet',
type: 'product',
name: 'iPad Pro',
price: 1099,
category: 'Electronics'
}).then(response => {
console.log('Saved:', response.id);
}).catch(err => {
console.error('Error:', err);
});
// Query local data
db.allDocs({include_docs: true}).then(result => {
result.rows.forEach(row => {
console.log(row.doc.name, '-', row.doc.price);
});
});
// Sync with remote CouchDB
const remote = new PouchDB('http://admin:password@localhost:5984/shop');
db.sync(remote, {
live: true,
retry: true
}).on('change', change => {
console.log('Changes synced:', change);
}).on('error', err => {
console.error('Sync error:', err);
});Common CouchDB Errors
1. {"error":"not_found","reason":"missing"}
The requested document doesn’t exist. Fix: Check the _id spelling. Use GET /database/_all_docs to list all document IDs.
2. {"error":"conflict","reason":"Document update conflict."}
You tried to update a document without providing the latest _rev. Fix: Always read the document first to get its current _rev, then include it in your PUT request.
3. {"error":"unauthorized","reason":"You are not authorized to access this db."}
Authentication required. Fix: Include credentials in the URL: http://user:pass@localhost:5984/db. Configure admin credentials in local.ini.
4. {"error":"file_error","reason":"Could not open database file"}
CouchDB cannot access its data directory. Fix: Check disk space, permissions, and the database_dir setting in local.ini.
5. View Build Failure
If your MapReduce view contains JavaScript errors, CouchDB reports the error when you query. Fix: Test your view functions in a JavaScript console before saving. Check _design/docs/_view/name?stale=ok for partially built views.
6. {"error":"bad_request","reason":"Referer header required for design document access."}
CORS settings restrict requests. Fix: Configure CORS in local.ini:
[cors]
origins = *
methods = GET, PUT, POST, DELETE, OPTIONS7. Replication Hangs
Large databases or frequent conflicts can stall replication. Fix: Check _active_tasks for replication status. Cancel and restart the replication. Ensure both ends have enough disk space.
Practice Questions
1. What is a document revision (_rev) in CouchDB?
Each edit to a document creates a new revision. The _rev is used for conflict detection — you must provide the latest _rev to update or delete a document. If someone else updated the document since you read it, your write will conflict.
2. How does CouchDB replication work?
CouchDB uses multi-master replication — any database can push changes to any other database. Replication can be one-shot (single sync) or continuous (live). Changes are tracked using a sequence number per database.
3. What is a MapReduce view?
A MapReduce view is a pre-computed index defined by JavaScript functions. The map function emits key-value pairs from documents. The optional reduce function aggregates results (like COUNT, SUM). Views are updated incrementally as documents change.
4. Challenge: Write a MapReduce view that finds all products with prices between 50 and 200.
// Map function
function(doc) {
if (doc.type === 'product' && doc.price >= 50 && doc.price <= 200) {
emit(doc.price, doc.name);
}
}Query with: GET /db/_design/products/_view/by_price_range
5. How does CouchDB handle conflicts differently from MongoDB?
CouchDB preserves all conflicting revisions and lets the application decide which to keep (multi-victor). MongoDB uses last-write-wins — the most recent write automatically overwrites, potentially losing data. CouchDB’s approach is better for offline-first applications.
Real-World Task: Build an Offline Shopping List App
Design a database for a shopping list that syncs across devices — similar to Doda Browser’s bookmarks sync:
// Document structure
{
"_id": "list_groceries",
"type": "shopping_list",
"name": "Weekly Groceries",
"owner": "alice@example.com",
"items": [
{"name": "Milk", "quantity": 2, "checked": false},
{"name": "Eggs", "quantity": 12, "checked": true},
{"name": "Bread", "quantity": 1, "checked": false}
],
"updated_at": "2026-06-07T15:00:00Z",
"shared_with": ["bob@example.com"]
}
// View: Lists shared with a specific user
// Map function
function(doc) {
if (doc.type === 'shopping_list' && doc.shared_with) {
doc.shared_with.forEach(function(email) {
emit(email, {name: doc.name, owner: doc.owner});
});
}
}
// View: Recently updated lists
// Map function
function(doc) {
if (doc.type === 'shopping_list' && doc.updated_at) {
emit(doc.updated_at, {name: doc.name, items_count: doc.items.length});
}
}FAQ
Try It Yourself
Install CouchDB and explore these management endpoints:
# Check server status
curl http://admin:password@localhost:5984/
# Response:
# {"couchdb":"Welcome","version":"3.3.3","uuid":"...","vendor":{"name":"Apache Software Foundation"}}
# List all databases
curl http://admin:password@localhost:5984/_all_dbs
# Get database stats
curl http://admin:password@localhost:5984/shop
# Response:
# {"db_name":"shop","doc_count":2,"doc_del_count":0,"update_seq":4,
# "purge_seq":0,"compact_running":false,"disk_size":12345,
# "data_size":678,"instance_start_time":"..."}
# View active tasks (including replications)
curl http://admin:password@localhost:5984/_active_tasks
# Get database changes feed (poll for updates)
curl http://admin:password@localhost:5984/shop/_changes?since=now&feed=continuousThese REST API patterns are used by Doda Browser for cross-device sync and by Durga Antivirus Pro for distributing signature updates to offline-protected systems via CouchDB replication.
What’s Next
Congratulations on completing this CouchDB tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro