OutWit.Database.Core.IndexedDb
1.0.0
dotnet add package OutWit.Database.Core.IndexedDb --version 1.0.0
NuGet\Install-Package OutWit.Database.Core.IndexedDb -Version 1.0.0
<PackageReference Include="OutWit.Database.Core.IndexedDb" Version="1.0.0" />
<PackageVersion Include="OutWit.Database.Core.IndexedDb" Version="1.0.0" />
<PackageReference Include="OutWit.Database.Core.IndexedDb" />
paket add OutWit.Database.Core.IndexedDb --version 1.0.0
#r "nuget: OutWit.Database.Core.IndexedDb, 1.0.0"
#:package OutWit.Database.Core.IndexedDb@1.0.0
#addin nuget:?package=OutWit.Database.Core.IndexedDb&version=1.0.0
#tool nuget:?package=OutWit.Database.Core.IndexedDb&version=1.0.0
OutWit.Database.Core.IndexedDb
IndexedDB storage provider for WitDatabase - enables Blazor WebAssembly support.
This package allows WitDatabase to run entirely in the browser with data persisted to IndexedDB.
Installation
<PackageReference Include="OutWit.Database.Core.IndexedDb" Version="1.0.0" />
Add the JavaScript files to your index.html:
<script src="_content/OutWit.Database.Core.IndexedDb/witdb-indexeddb.js"></script>
<script src="_content/OutWit.Database.Core.IndexedDb/witdb-indexeddb-index.js"></script>
Quick Start
Basic Usage
@page "/database-demo"
@inject IJSRuntime JSRuntime
@using OutWit.Database.Core.Builder
@using OutWit.Database.Core.IndexedDb
<h3>Database Demo</h3>
@code {
private WitDatabase? _db;
protected override async Task OnInitializedAsync()
{
_db = new WitDatabaseBuilder()
.WithIndexedDbStorage("MyAppDatabase", JSRuntime)
.WithBTree()
.WithTransactions()
.Build();
// Initialize storage (opens IndexedDB)
await (_db.Store as StorageIndexedDb)?.InitializeAsync()!;
}
private async Task SaveData()
{
var key = Encoding.UTF8.GetBytes("user:1");
var value = Encoding.UTF8.GetBytes("{\"name\":\"John\"}");
await _db!.PutAsync(key, value);
}
private async Task LoadData()
{
var key = Encoding.UTF8.GetBytes("user:1");
var value = await _db!.GetAsync(key);
if (value != null)
{
var json = Encoding.UTF8.GetString(value);
Console.WriteLine(json);
}
}
public async ValueTask DisposeAsync()
{
if (_db != null)
{
await _db.DisposeAsync();
}
}
}
With Encryption
var db = new WitDatabaseBuilder()
.WithIndexedDbStorage("SecureDatabase", JSRuntime)
.WithBTree()
.WithEncryption("user-password") // AES-GCM encryption
.WithTransactions()
.Build();
With MVCC
var db = new WitDatabaseBuilder()
.WithIndexedDbStorage("MvccDatabase", JSRuntime)
.WithBTree()
.WithMvcc()
.WithDefaultIsolationLevel(IsolationLevel.Snapshot)
.Build();
Compatibility
Storage Engines
| Engine | Compatible | Notes |
|---|---|---|
| B+Tree (WithBTree) | Yes | Recommended, full support |
| LSM-Tree (WithLsmTree) | No | Requires file system |
| In-Memory | Yes | No persistence |
Features
| Feature | Compatible | Notes |
|---|---|---|
| Basic CRUD | Yes | Full support |
| Transactions | Yes | Via TransactionalStore |
| MVCC | Yes | All isolation levels |
| Savepoints | Yes | Full support |
| Encryption | Yes | AES-GCM, BouncyCastle |
| Secondary Indexes | Yes | Full support via IndexedDB |
| File Locking | No | Not applicable |
| WAL/Journal | No | Single-file model |
Browsers
| Browser | Supported |
|---|---|
| Chrome 80+ | Yes |
| Firefox 75+ | Yes |
| Edge 80+ | Yes |
| Safari 14+ | Yes |
Secondary Indexes
Secondary indexes are automatically configured when using WithIndexedDbStorage(). Each index uses a separate object store within a dedicated IndexedDB database.
Automatic Configuration
// Indexes are automatically configured
var db = new WitDatabaseBuilder()
.WithIndexedDbStorage("MyDatabase", JSRuntime)
.WithBTree()
.Build();
// Index factory is automatically set up for IndexedDB
// Indexes will use "MyDatabase_indexes" database
Custom Index Database
var db = new WitDatabaseBuilder()
.WithIndexedDbStorage("MyDatabase", JSRuntime)
.WithIndexedDbIndexes(JSRuntime, "CustomIndexDb") // Override default
.WithBTree()
.Build();
Using Indexes
// Create index manager
var indexFactory = new SecondaryIndexFactoryIndexedDb(JSRuntime, "MyDatabase_indexes");
var indexManager = new IndexManager(indexFactory);
// Create a secondary index
var emailIndex = indexManager.CreateIndex("idx_email", isUnique: true);
// Add entries
emailIndex.Add(emailBytes, primaryKeyBytes);
// Find by index
var primaryKeys = emailIndex.Find(emailBytes);
API Reference
WitDatabaseBuilder Extensions
// Basic IndexedDB storage (auto-configures indexes)
builder.WithIndexedDbStorage(string databaseName, IJSRuntime jsRuntime)
// With custom page size
builder.WithIndexedDbStorage(string databaseName, IJSRuntime jsRuntime, int pageSize)
// Custom index database
builder.WithIndexedDbIndexes(IJSRuntime jsRuntime, string indexDatabaseName)
StorageIndexedDb
// Create storage directly
var storage = new StorageIndexedDb("DatabaseName", jsRuntime);
// Async initialization (recommended)
await storage.InitializeAsync();
// Check if database exists
bool exists = await storage.DatabaseExistsAsync();
// Properties
storage.DatabaseName // IndexedDB database name
storage.PageSize // Page size in bytes
storage.PageCount // Total number of pages
storage.IsInitialized // Whether storage is initialized
SecondaryIndexFactoryIndexedDb
// Create index factory
var factory = new SecondaryIndexFactoryIndexedDb(jsRuntime, "DatabaseName");
// Create indexes
var uniqueIndex = factory.CreateIndex("idx_unique", isUnique: true);
var nonUniqueIndex = factory.CreateIndex("idx_category", isUnique: false);
// Factory properties
factory.ProviderKey // "indexeddb"
IndexedDbInterop
Low-level IndexedDB operations (advanced usage):
var interop = new IndexedDbInterop(jsRuntime, "DatabaseName");
await interop.OpenAsync();
await interop.WritePageAsync(0, pageData);
var data = await interop.ReadPageAsync(0);
await interop.CloseAsync();
Limitations
No LSM-Tree Support
- LSM-Tree requires file system operations
- Use B+Tree instead
Single-Tab Access
- IndexedDB can be accessed from multiple tabs
- But WitDatabase does not coordinate between tabs
- Use single-tab or implement your own coordination
Storage Quota
- Browser limits IndexedDB storage (typically 50-100MB minimum)
- Check available quota with
navigator.storage.estimate()
Sync Operations
- Sync methods use async bridge internally
- Prefer async methods for better performance
Error Handling
Configuration Errors
try
{
var db = new WitDatabaseBuilder()
.WithIndexedDbStorage("MyDatabase", JSRuntime)
.WithLsmTree() // ERROR!
.Build();
}
catch (InvalidOperationException ex)
{
// "IndexedDB storage is not compatible with LSM-Tree engine..."
}
Page Size Mismatch
try
{
// First open with 4096
var db1 = new WitDatabaseBuilder()
.WithIndexedDbStorage("MyDatabase", JSRuntime, pageSize: 4096)
.Build();
db1.Dispose();
// Try to open with different page size
var db2 = new WitDatabaseBuilder()
.WithIndexedDbStorage("MyDatabase", JSRuntime, pageSize: 8192) // ERROR!
.Build();
}
catch (InvalidOperationException ex)
{
// "Database was created with page size 4096, but 8192 was requested..."
}
Troubleshooting
JavaScript Not Loaded
If you see errors about witDb or witDbIndex being undefined:
- Ensure both script tags are in
index.html - Make sure they are loaded before your Blazor app starts
- Check browser console for script loading errors
IndexedDB Not Available
// Check if IndexedDB is available
if (window.indexedDB == null)
{
// IndexedDB not supported or blocked
}
Quota Exceeded
try
{
await db.PutAsync(key, largeValue);
}
catch (JSException ex) when (ex.Message.Contains("quota"))
{
// Storage quota exceeded
// Consider cleanup or ask user to allow more storage
}
Performance Tips
Use Async Methods
// Preferred await db.PutAsync(key, value); // Avoid when possible (uses sync bridge) db.Put(key, value);Batch Operations
using var tx = db.BeginTransaction(); foreach (var item in items) { tx.Put(item.Key, item.Value); } tx.Commit();Appropriate Page Size
- Default 4096 is good for small records
- Use 8192-16384 for larger records
See Also
- OutWit.Database.Core - Core database library
- OutWit.Database.Core.BouncyCastle - ChaCha20 encryption
- MDN: IndexedDB
- Blazor JS Interop
- ROADMAP.md - Version 2.0 planned features
- ROADMAP.md - Main project roadmap
License
Licensed under the Apache License, Version 2.0. See LICENSE.
Attribution (optional)
If you use OutWit.Database.Core.IndexedDb in a product, a mention is appreciated (but not required), for example: "Powered by WitDatabase https://witdatabase.io/".
Trademark / Project name
"WitDatabase" and the WitDatabase logo are used to identify the official project by Dmitry Ratner.
You may:
- refer to the project name in a factual way (e.g., "built with WitDatabase");
- use the name to indicate compatibility (e.g., "WitDatabase-compatible").
You may not:
- use "WitDatabase" as the name of a fork or a derived product in a way that implies it is the official project;
- use the WitDatabase logo to promote forks or derived products without permission.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- Microsoft.JSInterop (>= 10.0.2)
- OutWit.Database.Core (>= 1.0.0)
-
net9.0
- Microsoft.JSInterop (>= 9.0.11)
- OutWit.Database.Core (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 30 | 1/25/2026 |