STORAGE PROVIDERS

Choose your
storage.

Keep a consistent document API.
Choose storage based on persistence, sharing, and query needs.

Provider behavior at a glance
CapabilityMemoryFileSystemRedisPostgreSQLMongoDBCosmos DB
Documents live inProcess memoryMemory + JSON fileRedisPostgreSQLMongoDBCosmos DB
PersistenceTemporaryPeriodic save (~5s)Write-through¹Committed writesCommitted writesCommitted writes
Shared across instancesNoNoYesYesYesYes
Index lifetimeUntil unloadRebuilt on reloadPersisted in RedisPersisted in PostgreSQLPersisted in MongoDBPersisted in Cosmos DB
Atomic batchesYesYes, saved immediatelyYesYesYesWithin Cosmos execution limits

¹ Redis persistence and eviction configuration determine durability. FileSystem uses one database owner per file; call Save() to flush explicitly.

Browser provider behavior at a glance
CapabilitylocalStoragesessionStorageIndexedDB
Snapshot lifetimeAcross browser sessionsCurrent tab session; survives reloadsAcross browser sessions
Ordinary writesExplicit Save()Explicit Save()Explicit Save()
Queries and indexesIn memory; rebuilt on reloadIn memory; rebuilt on reloadIn memory; rebuilt on reload
Atomic batchesSnapshot replacement with Web LocksSnapshot replacement with Web LocksSnapshot replacement in a transaction
Live shared readsNo; cached per ownerNo; cached per ownerNo; cached per owner

Browser storage policies and quotas apply. Use one owner per storage key and save pending writes before navigation.

01 / IN PROCESS

Memory

For temporary data, tests, and applications that don’t need storage to survive a restart. No external service to configure.

Package on NuGet ↗
INSTALL
dotnet add package Soenneker.Librarian.Memory
REGISTER
using Soenneker.Librarian.Memory.Registrars;

builder.Services.AddMemoryLibrarianDatabaseAsSingleton();

Scoped registrations are also available. A scoped memory database owns its own data.

02 / LOCAL JSON

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.

INSTALL & REGISTER
dotnet add package Soenneker.Librarian.FileSystem
C#
using Soenneker.Librarian.FileSystem.Registrars;

builder.Services.AddFileSystemLibrarianDatabaseAsSingleton();
APPSETTINGS.JSON
{
  "Librarian": {
    "FileSystem": { "FilePath": "data/librarian.json" }
  }
}
03 / SHARED STORAGE

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 ↗
INSTALL
dotnet add package Soenneker.Librarian.Redis
C#
using Soenneker.Librarian.Redis.Registrars;

builder.Services.AddRedisLibrarianDatabaseAsSingleton();
APPSETTINGS.JSON
{
  "Azure": { "Redis": { "ConnectionString": "localhost:6379" } },
  "Librarian": {
    "Redis": { "Key": "my-app:librarian" }
  }
}
04 / PERSISTENT SQL

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 ↗
INSTALL
dotnet add package Soenneker.Librarian.Postgres
C#
using Soenneker.Librarian.Postgres.Registrars;

builder.Services.AddPostgresLibrarianDatabaseAsSingleton();

Configure Librarian:Postgres:ConnectionString and Librarian:Postgres:Key.

05 / NATIVE DOCUMENTS

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.

INSTALL
dotnet add package Soenneker.Librarian.Mongo
C#
using Soenneker.Librarian.Mongo.Registrars;

builder.Services.AddMongoLibrarianDatabaseAsSingleton();
06 / AZURE DOCUMENTS

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.

INSTALL
dotnet add package Soenneker.Librarian.Cosmos
C#
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.

07 / HTTP DOCUMENTS

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.

PROJECT
Soenneker.Librarian.CouchDb
C#
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.

08 / BROWSER PERSISTENCE

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 ↗
INSTALL
dotnet add package Soenneker.Librarian.LocalStorage
C#
using Soenneker.Librarian.LocalStorage.Registrars;

builder.Services.AddLocalStorageLibrarianDatabaseAsScoped();

Optional Librarian:LocalStorage:Key defaults to librarian. Follow the browser lifecycle and save guidance below.

09 / TAB SESSION

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 ↗
INSTALL
dotnet add package Soenneker.Librarian.SessionStorage
C#
using Soenneker.Librarian.SessionStorage.Registrars;

builder.Services.AddSessionStorageLibrarianDatabaseAsScoped();

Optional Librarian:SessionStorage:Key defaults to librarian. Follow the browser lifecycle and save guidance below.

10 / BROWSER TRANSACTIONS

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 ↗
INSTALL
dotnet add package Soenneker.Librarian.IndexedDb
C#
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.

AFTER INTERACTIVE RENDERING
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 ↗