Choose your
storage.
Keep a consistent document API.
Choose storage based on persistence, sharing, and query needs.
| Capability | Memory | FileSystem | Redis | PostgreSQL | MongoDB | Cosmos DB |
|---|---|---|---|---|---|---|
| Documents live in | Process memory | Memory + JSON file | Redis | PostgreSQL | MongoDB | Cosmos DB |
| Persistence | Temporary | Periodic save (~5s) | Write-through¹ | Committed writes | Committed writes | Committed writes |
| Shared across instances | No | No | Yes | Yes | Yes | Yes |
| Index lifetime | Until unload | Rebuilt on reload | Persisted in Redis | Persisted in PostgreSQL | Persisted in MongoDB | Persisted in Cosmos DB |
| Atomic batches | Yes | Yes, saved immediately | Yes | Yes | Yes | Within Cosmos execution limits |
¹ Redis persistence and eviction configuration determine durability. FileSystem uses one database owner per file; call Save() to flush explicitly.
| Capability | localStorage | sessionStorage | IndexedDB |
|---|---|---|---|
| Snapshot lifetime | Across browser sessions | Current tab session; survives reloads | Across browser sessions |
| Ordinary writes | Explicit Save() | Explicit Save() | Explicit Save() |
| Queries and indexes | In memory; rebuilt on reload | In memory; rebuilt on reload | In memory; rebuilt on reload |
| Atomic batches | Snapshot replacement with Web Locks | Snapshot replacement with Web Locks | Snapshot replacement in a transaction |
| Live shared reads | No; cached per owner | No; cached per owner | No; cached per owner |
Browser storage policies and quotas apply. Use one owner per storage key and save pending writes before navigation.
Memory
For temporary data, tests, and applications that don’t need storage to survive a restart. No external service to configure.
Package on NuGet ↗dotnet add package Soenneker.Librarian.Memory
using Soenneker.Librarian.Memory.Registrars;
builder.Services.AddMemoryLibrarianDatabaseAsSingleton();
Scoped registrations are also available. A scoped memory database owns its own data.
FileSystem
In-memory access with a JSON file behind it. Designed for a single database owner, with periodic saves and immediate persistence for atomic batches.
Await database.Save() for an explicit flush. Dispose the database’s owner asynchronously to complete shutdown persistence.
dotnet add package Soenneker.Librarian.FileSystem
using Soenneker.Librarian.FileSystem.Registrars;
builder.Services.AddFileSystemLibrarianDatabaseAsSingleton();
{
"Librarian": {
"FileSystem": { "FilePath": "data/librarian.json" }
}
}
Redis
Shared documents and persisted indexes for multiple application instances. Reads fetch current Redis data; writes update documents and indexes together.
No RedisJSON, Redis Search, or Lua required. Use a non-evicting database; cluster deployments require Redis 8 or later.
Redis setup & guarantees ↗dotnet add package Soenneker.Librarian.Redis
using Soenneker.Librarian.Redis.Registrars;
builder.Services.AddRedisLibrarianDatabaseAsSingleton();
{
"Azure": { "Redis": { "ConnectionString": "localhost:6379" } },
"Librarian": {
"Redis": { "Key": "my-app:librarian" }
}
}
PostgreSQL
Shared persistent documents with SQL-backed filtering, sorting, paging, and aggregates. Writes and conditional batches commit immediately.
Indexes persist across restarts. Requires PostgreSQL 16 or later. Supply connection credentials through your application’s secret configuration.
PostgreSQL setup & queries ↗dotnet add package Soenneker.Librarian.Postgres
using Soenneker.Librarian.Postgres.Registrars;
builder.Services.AddPostgresLibrarianDatabaseAsSingleton();
Configure Librarian:Postgres:ConnectionString and Librarian:Postgres:Key.
MongoDB
Stores Document fields directly in native collections, with immediate writes, server-side LINQ, versioned writes, and atomic conditional batches across collections. MongoDB creates its _id index automatically. Configure secondary indexes explicitly with EnsureIndex or MongoDB tooling; reads do not create indexes.
Atomic batches require a MongoDB replica set or sharded cluster with transaction support. Configure Librarian:Mongo:ConnectionString, DatabaseName, and Key. Optional CollectionName defaults to librarian.
dotnet add package Soenneker.Librarian.Mongo
using Soenneker.Librarian.Mongo.Registrars;
builder.Services.AddMongoLibrarianDatabaseAsSingleton();
Cosmos DB for NoSQL
Creates the database and physical container on first access. Documents use native Cosmos point operations, automatic JSON indexing, and SDK LINQ queries. Librarian does not change indexing policies; multi-field ordering requires a composite index configured in Cosmos.
Documents use their own DocumentId and PartitionKey; each logical container has a physical Cosmos container. Explicit partition-scoped containers, cross-partition queries, native continuation paging, and versioned conditional writes are also supported. Conditional batches stay within one physical container and one partition and use the native TransactionalBatch API with ETag checks or create-only writes. Batches allow at most 100 writes; Cosmos request size and partition limits apply.
dotnet add package Soenneker.Librarian.Cosmos
using Soenneker.Librarian.Cosmos.Registrars;
builder.Services.AddCosmosLibrarianDatabaseAsSingleton();
Uses Azure:Cosmos:Endpoint, AccountKey, DatabaseName, DatabaseThroughput, DatabaseThroughputType, and the application's Environment. Optional Librarian:Cosmos:ContainerName and Key both default to librarian.
Database and container creation are enabled by default. The EnsureDatabaseOnFirstUse and EnsureContainerOnFirstUse settings remain available for pre-provisioned environments.
CouchDB
Creates a physical CouchDB database for each logical container on first access. Stores Document JSON directly, with revision-checked writes, partition-scoped handles, and explicit Mango indexes. Unloading releases local handles and preserves remote data.
Call EnsureIndex before indexed queries. CouchDB native collation applies. LINQ and atomic multi-document batches are unsupported; use JSON-path query methods and single-document revision checks.
Soenneker.Librarian.CouchDb
using Soenneker.Librarian.CouchDb.Registrars;
builder.Services.AddCouchDbLibrarianDatabaseAsSingleton();
Configure Librarian:CouchDb:Endpoint, optional Username and Password, and Key. DatabasePrefix and Key default to librarian. EnsureDatabaseOnFirstUse defaults to true; disable it for pre-provisioned databases. Requires CouchDB 3.5 or later.
localStorage
Small JSON snapshots stored in the browser for the current origin. Saved data persists across browser sessions, subject to browser storage policy. Writes require the Web Locks API in a secure context and cooperating writers.
Package on NuGet ↗dotnet add package Soenneker.Librarian.LocalStorage
using Soenneker.Librarian.LocalStorage.Registrars;
builder.Services.AddLocalStorageLibrarianDatabaseAsScoped();
Optional Librarian:LocalStorage:Key defaults to librarian. Follow the browser lifecycle and save guidance below.
sessionStorage
Small JSON snapshots scoped to the current origin and tab session. Saved data survives reloads; independent tabs have separate storage. Writes require the Web Locks API in a secure context and cooperating writers.
Package on NuGet ↗dotnet add package Soenneker.Librarian.SessionStorage
using Soenneker.Librarian.SessionStorage.Registrars;
builder.Services.AddSessionStorageLibrarianDatabaseAsScoped();
Optional Librarian:SessionStorage:Key defaults to librarian. Follow the browser lifecycle and save guidance below.
IndexedDB
Stores a complete JSON snapshot in IndexedDB, replacing it inside a read/write transaction. Documents and query indexes are loaded into memory; queries do not run against native IndexedDB indexes.
Package on NuGet ↗dotnet add package Soenneker.Librarian.IndexedDb
using Soenneker.Librarian.IndexedDb.Registrars;
builder.Services.AddIndexedDbLibrarianDatabaseAsScoped();
Optional Librarian:IndexedDb:Key defaults to librarian. Follow the browser lifecycle and save guidance below.
Using browser storage.
Register one browser provider as a scoped service and inject ILibrarianDatabase. Invoke operations only after interactive rendering, never during prerender. The provider includes its JavaScript module as a static web asset; no manual script tag is required.
Ordinary writes stay in memory until saved. Await database.Save() before navigation; do not rely on browser shutdown or circuit disposal for a final flush. Atomic batches persist the snapshot before publishing changes in memory.
var preferences = await database.GetContainer("preferences");
await preferences.AddItem("theme", """{"mode":"dark"}""");
await database.Save();
Use one database owner per storage key. Reads are cached, not synchronized between tabs or owners. After a concurrency conflict or an uncertain JavaScript interop result, discard the owner with IBrowserLibrarianDatabase.Discard() and reopen a fresh instance before retrying. Browser storage is unencrypted; quota, denied access, and unavailable browser features surface as errors.
Same API. Know the differences.
Memory, FileSystem, and the browser providers can fall back to local evaluation for unsupported queries. Redis and PostgreSQL reject unsupported expressions. MongoDB and Cosmos delegate LINQ translation to their native SDKs, including supported grouping, aggregates, and projections. Their native query semantics and translation limits apply; unsupported expressions are rejected without client-side fallback. Review query support before moving a workload between providers.
Compare query capabilities ↗