Project Awesome project awesome

CouchDB Client

Simple CouchDB client for Vapor.

Package 20 stars GitHub

CouchDB Client for Swift

Build on macOS Build on Ubuntu Test on Ubuntu

This is a simple library to work with CouchDB in Swift.


Features

  • Strict concurrency: CouchDBClient is an actor (Swift 6+)
  • Attachments API: upload, download, delete files/images
  • Mango queries and indexes
  • Document CRUD (create, read, update, delete)
  • Vapor and Hummingbird integration
  • Robust error handling

Supported Platforms & Swift Versions

  • Swift 6.0+ (actor-based concurrency)
  • Swift 5.x (use version 1.7.0)
  • macOS, Linux (tested on Ubuntu)
  • Compatible with Vapor 4 and Hummingbird

Testing

The integration test suite covers all major APIs, including attachments. It expects a running CouchDB instance and the COUCHDB_PASS environment variable. Run tests with:

COUCHDB_PASS=myPassword swift test

Documentation

Find documentation, examples, and tutorials here.

Installation

Swift Package Manager

Add the following to the dependencies section of your Package.swift:

dependencies: [
    .package(url: "https://github.com/makoni/couchdb-swift.git", from: "2.1.0"),
]

Initialization

let config = CouchDBClient.Config(
    couchProtocol: .http,
    couchHost: "127.0.0.1",
    couchPort: 5984,
    userName: "admin",
    userPassword: "",
    requestsTimeout: 30
)
let couchDBClient = CouchDBClient(config: config)

To avoid hardcoding your password, you can pass the COUCHDB_PASS parameter via the command line. For example, you can run your server-side Swift project as follows:

COUCHDB_PASS=myPassword /path/.build/x86_64-unknown-linux-gnu/release/Run

In this case, use the initializer without the userPassword parameter:

let config = CouchDBClient.Config(
    couchProtocol: .http,
    couchHost: "127.0.0.1",
    couchPort: 5984,
    userName: "admin",
    requestsTimeout: 30
)
let couchDBClient = CouchDBClient(config: config)

Usage examples

Uploading an Attachment

let response = try await couchDBClient.uploadAttachment(
    dbName: "myDatabase",
    docId: "docid",
    attachmentName: "image.png",
    data: imageData,
    contentType: "image/png",
    rev: "currentRev"
)
print("Attachment uploaded, new revision: \(response.rev)")

Downloading an Attachment

let attachmentData = try await couchDBClient.downloadAttachment(
    dbName: "myDatabase",
    docId: "docid",
    attachmentName: "image.png"
)
print("Downloaded attachment, size: \(attachmentData.count) bytes")

Deleting an Attachment

let deleteResponse = try await couchDBClient.deleteAttachment(
    dbName: "myDatabase",
    docId: "docid",
    attachmentName: "image.png",
    rev: "currentRev"
)
print("Attachment deleted, new revision: \(deleteResponse.rev)")

Define Your Document Model

// Example struct
struct ExpectedDoc: CouchDBRepresentable {
    var name: String
    var _id: String = UUID().uuidString
    var _rev: String?

    func updateRevision(_ newRevision: String) -> Self {
        return ExpectedDoc(name: name, _id: _id, _rev: newRevision)
    }
}

Insert Data

var testDoc = ExpectedDoc(name: "My name")

testDoc = try await couchDBClient.insert(
    dbName: "databaseName",
    doc: testDoc
)

print(testDoc) // testDoc has _id and _rev values now

Update Data

// get data from a database by document ID
var doc: ExpectedDoc = try await couchDBClient.get(fromDB: "databaseName", uri: "documentId")
print(doc)

// Update value
doc.name = "Updated name"

doc = try await couchDBClient.update(
    dbName: "databaseName",
    doc: doc
)

print(doc) // doc will have updated name and _rev values now

Delete Data

let response = try await couchDBClient.delete(fromDb: "databaseName", doc: doc)

guard let rev = doc._rev else {
    fatalError("Expected a revision after reading the document from CouchDB")
}

let rawResponse = try await couchDBClient.delete(
    fromDb: "databaseName",
    uri: doc._id,
    rev: rev
)

Get All Databases

let dbs = try await couchDBClient.getAllDBs()
print(dbs)
// prints: ["_global_changes", "_replicator", "_users", "yourDBname"]

Find Documents in a Database by Mango Query

let selector: [String: MangoValue] = [
    "type": .string("user"),
    "age": .comparison(.greaterThan(.int(30)))
]

let query = MangoQuery(
    selector: selector,
    fields: ["name", "email"],
    sort: [MangoSortField(field: "name", direction: .asc)],
    limit: 10,
    skip: 0
)

let docs: [ExpectedDoc] = try await couchDBClient.find(inDB: "databaseName", query: query)
print(docs)

Using with Vapor

Here's a simple tutorial for Vapor.


License

This project is licensed under the MIT License. See LICENSE for details.

Back to Vapor