CouchDB.NET
4.0.0
dotnet add package CouchDB.NET --version 4.0.0
NuGet\Install-Package CouchDB.NET -Version 4.0.0
<PackageReference Include="CouchDB.NET" Version="4.0.0" />
<PackageVersion Include="CouchDB.NET" Version="4.0.0" />
<PackageReference Include="CouchDB.NET" />
paket add CouchDB.NET --version 4.0.0
#r "nuget: CouchDB.NET, 4.0.0"
#:package CouchDB.NET@4.0.0
#addin nuget:?package=CouchDB.NET&version=4.0.0
#tool nuget:?package=CouchDB.NET&version=4.0.0
CouchDB.NET V4
Query CouchDB with LINQ! Inspired by Cosmos SDK.
For CouchDB.NET V3 docs: README.md
LINQ queries
C# query example:
// Setup
var clientOptions = new CouchClientOptions
{
JsonSerializerOptions = JsonSerializerOptions.Web
};
var client = new CouchClient(
endpoint: "http://localhost:5984",
credentials: new BasicCredentials("admin", "admin"),
clientOptions);
var rebels = await client.GetDatabase<Rebel>("rebels");
// Usage
var skywalkers = await rebels
.Where(r =>
r.Surname == "Skywalker" &&
(
r.Battles.All(b => b.Planet == "Naboo") ||
r.Battles.Any(b => b.Planet == "Death Star")
)
)
.OrderByDescending(r => r.Name)
.ThenByDescending(r => r.Age)
.Take(2)
.Select(
r => r.Name,
r => r.Age
})
.ToListAsync();
The produced Mango JSON:
{
"selector": {
"$and": [
{
"surname": "Skywalker"
},
{
"$or": [
{
"battles": {
"$allMatch": {
"planet": "Naboo"
}
}
},
{
"battles": {
"$elemMatch": {
"planet": "Death Star"
}
}
}
]
}
]
},
"sort": [
{
"name": "desc"
},
{
"age": "desc"
}
],
"limit": 2,
"fields": [
"name",
"age"
]
}
Index
- Getting started
- Queries
- Authentication
- Options
- Serialization behavior
- HttpClient customization
- Changes Feed
- Indexing
- Partitioned Databases
- Views
- Local (non-replicating) Documents
- Bookmark and Execution stats
- Users
- Replication
- Dependency Injection
Getting started
- Install it from NuGet: https://www.nuget.org/packages/CouchDB.NET
- Create a client providing the endpoint and authentication:
var client = new CouchClient( endpoint: "http://localhost:5984", credentials: new BasicCredentials("admin", "admin")) - Create a class or record for your documents:
public record Rebel(string Id, string Name) { public string Rev { get; init; } = null!; } - Get a database reference:
var rebels = client.GetDatabase<Rebel>("rebels"); - Query the database
var skywalkers = await rebels.Where(r => r.Surname == "Skywalker").ToListAsync();
Queries
The database class exposes all the implemented LINQ methods like Where and OrderBy,
those methods returns an IQueryable.
LINQ are supported natively to the following is possible:
var skywalkers =
from r in fixture.Rebels
where r.Surname == "Skywalker"
select r;
Selector
The selector is created when the method Where (IQueryable) is called.
If the Where method is not called in the expression, it will at an empty selector.
Combinations
| Mango | C# |
|---|---|
| $and | && |
| $or | || |
| $not | ! |
| $nor | !( || ) |
| $all | a.Contains(x) |
| $all | a.Contains(list) |
| $elemMatch | a.Any(condition) |
| $allMatch | a.All(condition) |
Conditions
| Mango | C# |
|---|---|
| $lt | < |
| $lte | ⇐ |
| $eq (implicit) | == |
| $ne | != |
| $gte | >= |
| $gt | > |
| $exists | o.FieldExists(s) |
| $type | o.IsCouchType(...) |
| $in | o.In(list) |
| $nin | !o.In(list) |
| $size | a.Count == x |
| $mod | n % x = y |
| $regex | s.IsMatch(rx) |
Native methods
| Mango | C# |
|---|---|
| limit | Take(n) |
| skip | Skip(n) |
| sort | OrderBy(..) |
| sort | OrderBy(..).ThenBy() |
| sort | OrderByDescending(..) |
| sort | OrderByDescending(..).ThenByDescending() |
| fields | Select(x ⇒ x.Prop1, x ⇒ x.Prop2) |
| fields | Convert<SourceType, SimplerType>() |
| use_index | UseIndex("design_document") |
| use_index | UseIndex(new [] { "design_document", "index_name" }) |
| r | WithReadQuorum(n) |
| bookmark | UseBookmark(s) |
| update | WithoutIndexUpdate() |
| stable | FromStable() |
| execution_stats | IncludeExecutionStats() |
| conflicts | IncludeConflicts() |
Composite methods
Some methods that are not directly supported by CouchDB are converted to a composition of supported ones!
| Input | Output |
|---|---|
| Min(d ⇒ d.Property) | OrderBy(d ⇒ d.Property).Take(1).Select(d ⇒ d.Property).Min() |
| Max(d ⇒ d.Property) | OrderByDescending(d ⇒ d.Property).Take(1).Select(d ⇒ d.Property).Max() |
| Sum(d ⇒ d.Property) | Select(d ⇒ d.Property).Sum() |
| Average(d ⇒ d.Property) | Select(d ⇒ d.Property).Average() |
| Any() | Take(1).Any() |
| Any(d ⇒ condition) | Where(d ⇒ condition).Take(1).Any() |
| All(d ⇒ condition) | Where(d ⇒ !condition).Take(1).Any() |
| Single() | Take(2).Single() |
| SingleOrDefault() | Take(2).SingleOrDefault() |
| Single(d ⇒ condition) | Where(d ⇒ condition).Take(2).Single() |
| SingleOrDefault(d ⇒ condition) | Where(d ⇒ condition).Take(2).SingleOrDefault() |
| First() | Take(1).First() |
| FirstOrDefault() | Take(1).FirstOrDefault() |
| First(d ⇒ condition) | Where(d ⇒ condition).Take(1).First() |
| FirstOrDefault(d ⇒ condition) | Where(d ⇒ condition).Take(1).FirstOrDefault() |
| Last() | Where(d ⇒ Last() |
| LastOrDefault() | LastOrDefault() |
| Last(d ⇒ condition) | Where(d ⇒ condition).Last() |
| LastOrDefault(d ⇒ condition) | Where(d ⇒ condition).LastOrDefault() |
INFO: Also Select(d => d.Property), Min and Max are supported.
WARN: Since Max and Min use sort, an index must be created.
All other IQueryable methods
IQueryable methods that are not natively supported will throw an exception.
Authentication
There are four types of authentication supported: Basic, Cookie, Proxy and JWT to pass in the CouchClient constructor.
// Basic
var cred = new BasicCredentials("root", "relax");
// Cookie
var cred = new CookieCredentials("root", "relax");
var cred = new CookieCredentials("root", "relax", cookieDuration);
// Proxy
var cred = new ProxyCredentials("root", Roles: ["role1", "role2"])
// JTW
var cred = new JwtCredentials("token")
var cred = new JwtCredentials(async () => await NewTokenAsync())
Options
Options can be specified when creating the CouchClient:
var clientOptions = new CouchClientOptions
{
// By default, if a warning is returned from CouchDB, an exception is thrown.
// It can be enabled/disabled per query by calling .With/WithoutQueryWarningException() in LINQ
ThrowOnQueryWarning = true,
// By default, the SDK uses `JsonSerializerOptions.Web` for documents
JsonSerializerOptions = myCustomJsonSerializerOptions,
// Custom HttpClient used by the SDK
HttpClient = myCustomHttpClient
}
Serialization behavior
While the SDK aims to be as close to the APIs as possible, some work has been done to improve the developer experience.
IdandRevproperties are mapped to_idand_revfields automatically.ReadItemAsyncreturn aReadItemResponse<T>to separate the document from metadata.- When updating a document, the
Revproperty is stripped automatically, and the method parameter is used instead. - The
JsonSerializerOptionsprovided are only used for user documents. For SDK objects, different options are used. They can't be changed, and they use source generators for better performance.
HttpClient customization
You have full control over the HttpClient used by the SDK. The SDK generates the correct request, serialize and deserialize.
Any other behavior can be customized by providing your own HttpClient instance in the CouchClientOptions.
Bulk operations
You can execute multiple operations in a single request using ExecuteBulkItemOperationsAsync.
BulkItemOperation[] op =
[
BulkItemOperation.Add(doc),
BulkItemOperation.Update(doc, id, rev),
BulkItemOperation.Delete(id, rev)
];
var results = await database.ExecuteBulkItemOperationsAsync(op);
Changes Feed
The following feed modes are supported: normal, longpool and continuous.
Also, all options and filter types are supported.
Continuous mode is probably the most useful, and it's implemented with the new IAsyncEnumerable.
var tokenSource = new CancellationTokenSource();
await foreach (var change in _rebels.GetContinuousChangesAsync(options: null, filter: null, tokenSource.Token))
{
if (/* ... */) {
tokenSource.Cancel();
}
}
Feed Options
// Example
var options = new ChangesFeedOptions
{
Descending = true,
Limit = 10,
Since = "now",
IncludeDocs = true
};
ChangesFeedResponse<Rebel> changes = await GetChangesAsync(options);
Feed Filter
// _doc_ids
var filter = ChangesFeedFilter.DocumentIds(new[] { "luke", "leia" });
// _selector
var filter = ChangesFeedFilter.Selector<Rebel>(rebel => rebel.Age == 19);
// _design
var filter = ChangesFeedFilter.Design();
// _view
var filter = ChangesFeedFilter.View(view);
// Design document filter with custom query parameters
var filter = ChangesFeedFilter.DesignDocument("replication/by_partition",
new Dictionary<string, string> { { "partition", "skywalker" } });
// Use
ChangesFeedResponse<Rebel> changes = await GetChangesAsync(options: null, filter);
Design Document Filters with Query Parameters
For partitioned databases or custom filtering logic, you can use design document filters with query parameters:
// Create a design document in CouchDB with a filter function
// _design/replication
{
"filters": {
"by_partition": function(doc, req) {
var partition = req.query.partition;
return doc._id.indexOf(partition + ':') === 0;
}
}
}
// Use the filter with query parameters
var filter = ChangesFeedFilter.DesignDocument("replication/by_partition",
new Dictionary<string, string> { { "partition", "businessId123" } });
await foreach (var change in db.GetContinuousChangesAsync(null, filter, cancellationToken))
{
// Process changes from specific partition
}
// Or pass query parameters via options
var options = new ChangesFeedOptions
{
Filter = "replication/by_partition",
QueryParameters = new Dictionary<string, string> { { "partition", "businessId123" } }
};
var changes = await db.GetChangesAsync(options);
Indexing
It is possible to create indexes to use when querying.
// Basic index creation
await _rebels.CreateIndexAsync("rebels_index", b => b
.IndexBy(r => r.Surname))
.ThenBy(r => r.Name));
// Descending index creation
await _rebels.CreateIndexAsync("rebels_index", b => b
.IndexByDescending(r => r.Surname))
.ThenByDescending(r => r.Name));
Index Options
// Specifies the design document and/or whether a JSON index is partitioned or global
await _rebels.CreateIndexAsync("rebels_index", b => b
.IndexBy(r => r.Surname),
new IndexOptions()
{
DesignDocument = "surnames_ddoc",
Partitioned = true
});
Partial Indexes
// Create an index which excludes documents at index time
await _rebels.CreateIndexAsync("skywalkers_index", b => b
.IndexBy(r => r.Name)
.Where(r => r.Surname == "Skywalker");
Indexes operations
// Get the list of indexes
var indexes = await _rebels.GetIndexesAsync();
// Delete an indexes
await _rebels.DeleteIndexAsync(indexes[0]);
// or
await _rebels.DeleteIndexAsync("surnames_ddoc", name: "surnames");
Partitioned Databases
CouchDB partitioned databases allow you to optimize query performance by grouping related documents together using a partition key.
Creating a Partitioned Database
// Create a partitioned database
var rebels = await client.CreateDatabaseAsync<Rebel>("rebels", partitioned: true);
// Or with GetOrCreateDatabaseAsync
var rebels = await client.GetOrCreateDatabaseAsync<Rebel>("rebels", partitioned: true);
Partitioned Document IDs
In partitioned databases, document IDs must follow the format: {partition_key}:{document_id}
var luke = new Rebel
{
Id = "skywalker:luke", // partition key is "skywalker"
Name = "Luke",
Surname = "Skywalker"
};
await rebels.AddAsync(luke);
Getting Partition Information
// Get metadata about a specific partition
var partitionInfo = await rebels.GetPartitionInfoAsync("skywalker");
Console.WriteLine($"Documents in partition: {partitionInfo.DocCount}");
Console.WriteLine($"Partition size: {partitionInfo.Sizes.Active} bytes");
Querying Partitioned Databases
Partition-specific queries are more efficient as they only scan documents within the partition:
// Query a partition using Mango selector
var skywalkers = await rebels.QueryPartitionAsync("skywalker", new
{
selector = new { name = new { $gt = "A" } },
sort = new[] { "name" }
});
// Or with JSON string
var json = "{\"selector\": {\"name\": {\"$gt\": \"A\"}}}";
var results = await rebels.QueryPartitionAsync("skywalker", json);
// Get all documents in a partition
var allSkywalkers = await rebels.GetPartitionAllDocsAsync("skywalker");
Checking if Database is Partitioned
var dbInfo = await rebels.GetInfoAsync();
bool isPartitioned = dbInfo.Props?.Partitioned ?? false;
Partitioned Indexes
When creating indexes for partitioned databases, specify whether the index should be partitioned or global:
// Create a partitioned index (default for partitioned databases)
await rebels.CreateIndexAsync("name_index",
b => b.IndexBy(r => r.Name),
new IndexOptions { Partitioned = true });
// Create a global index (queries across all partitions)
await rebels.CreateIndexAsync("global_index",
b => b.IndexBy(r => r.Age),
new IndexOptions { Partitioned = false });
Views
It's possible to query a view with the following:
var options = new CouchViewOptions<string[]>
{
StartKey = new[] {"Luke", "Skywalker"},
IncludeDocs = true
};
var viewRows = await _rebels.GetViewAsync<string[], RebelView>("jedi", "by_name", options);
You can also query a view with multiple options to get multiple results:
var lukeOptions = new CouchViewOptions<string[]>
{
Key = new[] {"Luke", "Skywalker"},
IncludeDocs = true
};
var yodaOptions = new CouchViewOptions<string[]>
{
Key = new[] {"Yoda"},
IncludeDocs = true
};
var queries = new[]
{
lukeOptions,
yodaOptions
};
var results = await _rebels.GetViewQueryAsync<string[], RebelView>("jedi", "by_name", queries);
var lukeRows = results[0];
var yodaRows = results[1];
Local (non-replicating) Documents
The Local (non-replicating) document interface allows you to create local documents that are not replicated to other databases.
var docId = "settings";
var settings = new RebelSettings
{
Id = docId,
IsActive = true
};
// Create
await _rebels.LocalDocuments.CreateOrUpdateAsync(settings);
// Get by ID
settings = await _rebels.LocalDocuments.GetJsonAsync<RebelSettings>(docId);
// Get all
var docs = await local.GetAsync();
// Search
var searchOpt = new LocalDocumentsOptions
{
Descending = true,
Limit = 10,
Conflicts = true
};
var docs = await local.GetAsync(searchOpt);
Bookmark and Execution stats
Bookmark and execution stats can be found in the CouchList<T> result from ToListAsync.
var allRebels = await rebels.ToListAsync();
foreach(var r in allRebels)
{
...
}
var b = allRebels.Bookmark;
var ex = allRebels.ExecutionStats; // .IncludeExecutionStats() must be called
Users
The driver natively support the _users database.
var users = client.GetUsersDatabase();
var luke = await users.CreateAsync(new CouchUser(name: "luke", password: "lasersword"));
It's possible to extend CouchUser for store custom info.
var users = client.GetUsersDatabase<CustomUser>();
var luke = await users.CreateAsync(new CustomUser(name: "luke", password: "lasersword"));
Replication
The driver provides the ability to configure and cancel replication between databases.
var options = new ConfigureReplicationOptions
{
Continuous = true
}
var success = await client.ConfigureReplicationAsync("anakin", "jedi", options);
if (success)
{
await client.CancelReplicationAsync("anakin", "jedi");
}
It is also possible to specify a selector to apply to the replication
new ConfigureReplicationOptions { Selector = new { designation = "FN-2187" } };
Credentials can be specified as follows
new ConfigureReplicationOptions
{
SourceCredentials = new CouchReplicationBasicCredentials(username: "luke", password: "r2d2")
};
Dependency Injection
CouchClient, and CouchDatabase<T> must be registered as a singleton services.
There is no built-in extension method, but you can create your own:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCouchClient(
this IServiceCollection services,
string endpoint,
CouchCredentials credentials,
CouchClientOptions? options = null)
{
var client = new CouchClient(endpoint, credentials, options);
services.AddSingleton(client);
return services;
}
public static IServiceCollection AddCouchDatabase<T>(
this IServiceCollection services,
string databaseName)
{
services.AddSingleton(provider =>
{
var client = provider.GetRequiredService<CouchClient>();
return client.GetDatabase<T>(databaseName);
});
return services;
}
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (9)
Showing the top 5 NuGet packages that depend on CouchDB.NET:
| Package | Downloads |
|---|---|
|
CouchDB.NET.DependencyInjection
Dependency injection utilities for CouchDB.NET |
|
|
M5x.DEC.Infra
Package Description |
|
|
M5x.Couch
Macula CouchDB Abstraction |
|
|
Furly.Extensions.CouchDb
CouchDB storage abstraction used by Furly and friends |
|
|
ServiceComponents.Infrastructure.CouchDB
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 4.0.0 | 89 | 1/11/2026 | |
| 4.0.0-preview1 | 80 | 1/3/2026 | |
| 3.7.0 | 885 | 12/3/2025 | |
| 3.6.1 | 37,099 | 4/30/2024 | |
| 3.6.0 | 4,558 | 3/11/2024 | |
| 3.4.0 | 35,421 | 6/20/2023 | |
| 3.3.1 | 103,363 | 10/26/2022 | |
| 3.3.0 | 7,671 | 10/19/2022 | |
| 3.2.0 | 14,800 | 7/3/2022 | |
| 3.2.0-Preview4 | 2,525 | 6/13/2022 | |
| 3.2.0-Preview3 | 1,446 | 5/16/2022 | |
| 3.2.0-Preview2 | 821 | 4/6/2022 | |
| 3.2.0-Preview | 10,824 | 3/6/2022 | |
| 3.1.1 | 57,588 | 10/14/2021 | |
| 3.1.0 | 14,669 | 3/20/2021 | |
| 3.0.1 | 1,274 | 3/10/2021 | |
| 3.0.0 | 1,403 | 3/9/2021 | |
| 2.1.0 | 5,043 | 9/19/2020 | |
| 2.0.2 | 1,619 | 7/18/2020 | |
| 2.0.0 | 1,490 | 7/14/2020 | |
| 1.2.2 | 8,454 | 7/2/2020 | |
| 1.2.1 | 9,116 | 2/25/2020 | |
| 1.2.0 | 2,703 | 1/24/2020 | |
| 1.1.5 | 5,194 | 12/19/2019 | |
| 1.1.4 | 2,397 | 8/19/2019 | |
| 1.1.3 | 1,455 | 6/14/2019 | |
| 1.1.2 | 1,272 | 6/8/2019 | |
| 1.1.1 | 1,249 | 6/2/2019 | |
| 1.1.0 | 1,384 | 5/5/2019 | |
| 1.0.2 | 1,387 | 5/2/2019 | |
| 1.0.1 | 1,252 | 4/27/2019 | |
| 1.0.1-beta.4 | 515 | 4/25/2019 | |
| 1.0.1-beta.3 | 498 | 4/10/2019 | |
| 1.0.1-beta.2 | 493 | 4/3/2019 | |
| 1.0.0 | 1,315 | 3/31/2019 | |
| 0.6.0-beta | 1,107 | 3/18/2019 | |
| 0.5.2-pre-alpha | 1,805 | 7/18/2018 | |
| 0.5.1-pre-alpha | 1,711 | 7/13/2018 |