Socket
Book a DemoInstallSign in
Socket

github.com/SwanHtetAungPhyo/couchdb

Package Overview
Dependencies
Alerts
File Explorer
Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

github.com/SwanHtetAungPhyo/couchdb

v0.1.2
Source
Go
Version published
Created
Source

CouchDB Go Client

Go Version License Go Report Card GoDoc

A comprehensive and intuitive Go client library for Apache CouchDB, built on top of the Resty HTTP client. This library provides a clean, fluent API for all CouchDB operations including documents, views, design documents, and database administration.

✨ Features

  • 🚀 Full CouchDB API Coverage - Complete support for all CouchDB operations
  • 🏗️ Fluent Query Builder - Intuitive builder pattern for complex view queries
  • 📦 Bulk Operations - Efficient bulk document operations
  • 🎯 Context Support - Full context.Context support for all operations
  • 🔍 Advanced View Queries - Comprehensive view query options and helpers
  • 📋 Design Document Management - Full CRUD operations for design documents
  • Built-in Error Handling - Structured error responses with CouchDB error details
  • 🛠️ Database Administration - Database creation, deletion, compaction, and maintenance

🚀 Quick Start

Installation

go get github.com/SwanHtetAungPhyo/couchdb

Basic Usage

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/SwanHtetAungPhyo/couchdb"
)

func main() {
    // Create client
    client := couchdb.NewClient("http://localhost:5984", &couchdb.ClientOptions{
        Username: "admin",
        Password: "password",
        Timeout:  30 * time.Second,
    })

    // Get database reference
    db := client.DB("myapp")
    ctx := context.Background()

    // Create a document
    user := map[string]interface{}{
        "name":  "John Doe",
        "email": "john@example.com",
        "age":   30,
    }

    result, err := db.Put(ctx, user)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Created user: %s\n", result.ID)

    // Retrieve the document
    doc, err := db.Get(ctx, result.ID)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("User: %+v\n", doc.Data)
}

📖 Documentation

Client Configuration

client := couchdb.NewClient("http://localhost:5984", &couchdb.ClientOptions{
    Username: "admin",           // Basic auth username
    Password: "password",        // Basic auth password
    Timeout:  30 * time.Second, // Request timeout
    Debug:    false,            // Enable debug logging
})

Document Operations

db := client.DB("mydb")
ctx := context.Background()

// Create document
doc := map[string]interface{}{"name": "Alice", "age": 25}
result, err := db.Put(ctx, doc)

// Get document
doc, err := db.Get(ctx, "doc-id")

// Update document
updated := map[string]interface{}{
    "_id": doc.ID,
    "_rev": doc.Rev,
    "name": "Alice Smith",
    "age": 26,
}
result, err := db.Update(ctx, doc.ID, updated)

// Delete document
err = db.Delete(ctx, doc.ID, doc.Rev)

// Bulk operations
docs := []interface{}{
    map[string]interface{}{"name": "Bob"},
    map[string]interface{}{"name": "Carol"},
}
results, err := db.Bulk(ctx, docs)

View Queries

Simple View Queries

// Get all results
result, err := db.ViewAll(ctx, "users", "by_age", true)

// Query by key
result, err := db.ViewByKey(ctx, "users", "by_city", "New York")

// Query by range
result, err := db.ViewByKeyRange(ctx, "users", "by_age", 25, 35)

// Get reduced results
result, err := db.ViewReduce(ctx, "stats", "count_by_city", 1)

Advanced Query Builder

result, err := db.NewViewQuery("users", "by_age").
    StartKey(25).
    EndKey(40).
    IncludeDocs(true).
    Limit(10).
    Descending(true).
    Execute(ctx, db)

// Complex query with multiple options
result, err := db.NewViewQuery("products", "by_category").
    Key("electronics").
    IncludeDocs(true).
    Stale("update_after").
    Group(true).
    Execute(ctx, db)

Direct View Query with Options

opts := &couchdb.ViewOptions{
    StartKey:    "A",
    EndKey:      "M",
    IncludeDocs: true,
    Limit:       50,
    Descending:  false,
}

result, err := db.View(ctx, "mydesign", "myview", opts)

Design Documents

// Create design document with views
designDoc := &couchdb.DesignDocument{
    Language: "javascript",
    Views: map[string]*couchdb.View{
        "by_name": {
            Map: `function(doc) {
                if (doc.name) {
                    emit(doc.name, doc);
                }
            }`,
        },
        "count_by_status": {
            Map: `function(doc) {
                if (doc.status) {
                    emit(doc.status, 1);
                }
            }`,
            Reduce: "_count",
        },
    },
}

// Save design document
result, err := db.PutDesignDoc(ctx, "users", designDoc)

// Get design document
designDoc, err := db.GetDesignDoc(ctx, "users")

// List all design documents
designs, err := db.ListDesignDocs(ctx)

// Delete design document
err = db.DeleteDesignDoc(ctx, "users", rev)

Database Administration

// Server info
info, err := client.Info(ctx)
fmt.Printf("CouchDB %s\n", info.Version)

// List databases
dbs, err := client.AllDbs(ctx)

// Create/delete database
err = client.CreateDB(ctx, "newdb")
err = client.DeleteDB(ctx, "olddb")

// Database info
dbInfo, err := db.Info(ctx)
fmt.Printf("Documents: %d\n", dbInfo.DocCount)

// Maintenance operations
err = db.Compact(ctx)                    // Compact database
err = db.CompactDesignDoc(ctx, "users")  // Compact design doc
err = db.ViewCleanup(ctx)                // Clean up old view files

Changes Feed

changes, err := db.Changes(ctx, map[string]interface{}{
    "since":        "now",
    "feed":         "normal",
    "include_docs": true,
    "limit":        100,
})

Error Handling

doc, err := db.Get(ctx, "nonexistent")
if err != nil {
    if couchErr, ok := err.(*couchdb.Error); ok {
        switch couchErr.StatusCode {
        case 404:
            fmt.Println("Document not found")
        case 409:
            fmt.Println("Document conflict")
        default:
            fmt.Printf("CouchDB error: %s - %s\n", couchErr.Type, couchErr.Reason)
        }
    }
}

🔧 Complete Examples

📝 Blog Application Example
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/SwanHtetAungPhy"
)

type BlogPost struct {
    ID        string    `json:"_id,omitempty"`
    Rev       string    `json:"_rev,omitempty"`
    Title     string    `json:"title"`
    Content   string    `json:"content"`
    Author    string    `json:"author"`
    Tags      []string  `json:"tags"`
    Published bool      `json:"published"`
    CreatedAt time.Time `json:"created_at"`
}

func main() {
    client := couchdb.NewClient("http://localhost:5984", &couchdb.ClientOptions{
        Username: "admin",
        Password: "password",
    })

    db := client.DB("blog")
    ctx := context.Background()

    // Create database
    err := client.CreateDB(ctx, "blog")
    if err != nil {
        log.Printf("Database might already exist: %v", err)
    }

    // Create design document for blog queries
    designDoc := &couchdb.DesignDocument{
        Views: map[string]*couchdb.View{
            "by_author": {
                Map: `function(doc) {
                    if (doc.author && doc.published) {
                        emit(doc.author, {
                            title: doc.title,
                            created_at: doc.created_at
                        });
                    }
                }`,
            },
            "by_tag": {
                Map: `function(doc) {
                    if (doc.tags && doc.published) {
                        doc.tags.forEach(function(tag) {
                            emit(tag, {
                                title: doc.title,
                                author: doc.author
                            });
                        });
                    }
                }`,
            },
            "published_count": {
                Map: `function(doc) {
                    if (doc.published !== undefined) {
                        emit(doc.published, 1);
                    }
                }`,
                Reduce: "_count",
            },
        },
    }

    _, err = db.PutDesignDoc(ctx, "blog", designDoc)
    if err != nil {
        log.Fatal(err)
    }

    // Create blog posts
    posts := []BlogPost{
        {
            Title:     "Getting Started with CouchDB",
            Content:   "CouchDB is a NoSQL database...",
            Author:    "alice",
            Tags:      []string{"couchdb", "nosql", "database"},
            Published: true,
            CreatedAt: time.Now(),
        },
        {
            Title:     "Advanced CouchDB Views",
            Content:   "Views in CouchDB are powerful...",
            Author:    "bob",
            Tags:      []string{"couchdb", "views", "advanced"},
            Published: true,
            CreatedAt: time.Now(),
        },
        {
            Title:     "Draft Post",
            Content:   "This is a draft...",
            Author:    "alice",
            Tags:      []string{"draft"},
            Published: false,
            CreatedAt: time.Now(),
        },
    }

    // Bulk insert posts
    docs := make([]interface{}, len(posts))
    for i, post := range posts {
        docs[i] = post
    }

    results, err := db.Bulk(ctx, docs)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Created %d blog posts\n", len(results))

    // Query posts by author
    authorPosts, err := db.ViewByKey(ctx, "blog", "by_author", "alice")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Alice has %d published posts\n", len(authorPosts.Rows))

    // Query posts by tag with limit
    tagPosts, err := db.NewViewQuery("blog", "by_tag").
        Key("couchdb").
        Limit(5).
        Execute(ctx, db)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d posts tagged 'couchdb'\n", len(tagPosts.Rows))

    // Get publication statistics
    stats, err := db.ViewReduce(ctx, "blog", "published_count", 0)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Publication stats:")
    for _, row := range stats.Rows {
        status := "draft"
        if row.Key.(bool) {
            status = "published"
        }
        fmt.Printf("  %s: %.0f posts\n", status, row.Value.(float64))
    }
}
🛒 E-commerce Product Catalog Example
package main

import (
	"context"
	"fmt"
	"github.com/SwanHtetAungPhyo/couchdb/couchdb"
	"log"

	"github.com/yourusername/couchdb-go"
)

type Product struct {
	ID          string  `json:"_id,omitempty"`
	Rev         string  `json:"_rev,omitempty"`
	Name        string  `json:"name"`
	Category    string  `json:"category"`
	Price       float64 `json:"price"`
	InStock     bool    `json:"in_stock"`
	Description string  `json:"description"`
	Brand       string  `json:"brand"`
}

func main() {
	client := couchdb.NewClient("http://localhost:5984", nil)
	db := client.DB("products")
	ctx := context.Background()

	// Create products design document
	designDoc := &couchdb.DesignDocument{
		Views: map[string]*couchdb.View{
			"by_category": {
				Map: `function(doc) {
                    if (doc.category && doc.in_stock) {
                        emit([doc.category, doc.price], {
                            name: doc.name,
                            brand: doc.brand,
                            price: doc.price
                        });
                    }
                }`,
			},
			"by_price_range": {
				Map: `function(doc) {
                    if (doc.price && doc.in_stock) {
                        emit(doc.price, {
                            name: doc.name,
                            category: doc.category,
                            brand: doc.brand
                        });
                    }
                }`,
			},
			"total_value_by_category": {
				Map: `function(doc) {
                    if (doc.category && doc.price && doc.in_stock) {
                        emit(doc.category, doc.price);
                    }
                }`,
				Reduce: "_sum",
			},
		},
	}

	_, err := db.PutDesignDoc(ctx, "products", designDoc)
	if err != nil {
		log.Fatal(err)
	}

	// Sample products
	products := []Product{
		{Name: "iPhone 14", Category: "electronics", Price: 999.99, InStock: true, Brand: "Apple"},
		{Name: "MacBook Pro", Category: "electronics", Price: 2399.99, InStock: true, Brand: "Apple"},
		{Name: "Samsung TV", Category: "electronics", Price: 1299.99, InStock: false, Brand: "Samsung"},
		{Name: "Coffee Maker", Category: "appliances", Price: 79.99, InStock: true, Brand: "Breville"},
		{Name: "Blender", Category: "appliances", Price: 129.99, InStock: true, Brand: "Vitamix"},
	}

	// Insert products
	docs := make([]interface{}, len(products))
	for i, product := range products {
		docs[i] = product
	}

	_, err = db.Bulk(ctx, docs)
	if err != nil {
		log.Fatal(err)
	}

	// Query electronics under $2000
	electronics, err := db.NewViewQuery("products", "by_category").
		StartKey([]interface{}{"electronics", 0}).
		EndKey([]interface{}{"electronics", 2000}).
		IncludeDocs(false).
		Execute(ctx, db)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Electronics under $2000:\n")
	for _, row := range electronics.Rows {
		product := row.Value.(map[string]interface{})
		fmt.Printf("  %s - $%.2f (%s)\n",
			product["name"], product["price"], product["brand"])
	}

	// Get products in price range $100-$500
	priceRange, err := db.NewViewQuery("products", "by_price_range").
		StartKey(100).
		EndKey(500).
		Execute(ctx, db)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("\nProducts $100-$500:\n")
	for _, row := range priceRange.Rows {
		product := row.Value.(map[string]interface{})
		fmt.Printf("  %s - $%.2f (%s)\n",
			product["name"], row.Key, product["category"])
	}

	// Get total inventory value by category
	categoryTotals, err := db.ViewReduce(ctx, "products", "total_value_by_category", 1)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("\nInventory value by category:\n")
	for _, row := range categoryTotals.Rows {
		fmt.Printf("  %s: $%.2f\n", row.Key, row.Value)
	}
}

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  • Fork the repository
  • Create your feature branch (git checkout -b feature/amazing-feature)
  • Commit your changes (git commit -m 'Add some amazing feature')
  • Push to the branch (git push origin feature/amazing-feature)
  • Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙋‍♂️ Support

Made with ❤️ for the Go and CouchDB communities

FAQs

Package last updated on 01 Jul 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.