Awesome
BlazorIndexedDbJs
This is a Blazor library for accessing IndexedDB, it uses Jake Archibald's idb library for handling access to IndexedDB API.
It tries to implement IndexedDB API with same classes and function names when possible, so you can use public documentation
This library was originally a fork from William Tulloch library Blazor.IndexedDB.
ATTENTION Current 2.3.x versions are development versions, it can suffer major changes between minor version, 2.4.x versions will be considered stable.
API
IDBDatabase
Properties
name
public string Name
version
public int Version
objectStoreNames
public List<string> ObjectStoreNames
objectStores
public IList<IDBObjectStore> ObjectStores
Constructor
public IDBDatabase(IJSRuntime jsRuntime)
Methods
open()
public async Task Open();
deleteDatabase()
public async Task DeleteDatabase();
objectStore()
public IDBObjectStore ObjectStore(string storeName);
IDBObjectStore
Properties
name
public string Name
keyPath
public string? KeyPath
autoIncrement
public bool AutoIncrement
Indexes
public IList<IDBIndex> Indexes
IDBDatabase
public IDBDatabase IDBDatabase
Constructor
public IDBObjectStore(IDBDatabase idbDatabase);
Methods
add()
public async Task Add<TData>(TData data);
public async Task Add<TData, TKey>(TData data, TKey key);
put()
public async Task Put<TData>(TData data);
public async Task Put<TData, TKey>(TData data, TKey key);
delete()
public async Task Delete<TKey>(TKey key);
clear()
public async Task ClearStore();
Batch (add/put/delete) functions
public async Task BatchAdd<TData>(TData[] data);
public async Task BatchPut<TData>(TData[] data);
public async Task BatchDelete<TKey>(TKey[] key);
count()
public async Task<int> Count();
public async Task<int> Count<TKey>(TKey key);
public async Task<int> Count<TKey>(IDBKeyRange<TKey> key);
get()
public async Task<TResult?> Get<TKey, TResult>(TKey key);
getAll()
public async Task<List<TResult>> GetAll<TResult>(int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey[] key);
getAllKeys()
public async Task<List<TResult>> GetAllKeys<TResult>(int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey[] key);
Query
public async Task<List<TResult>> Query<TResult>(string filter, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, TKey key, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, IDBKeyRange<TKey> key, int? count = null, int? skip = null)
IDBIndex
Properties
name
public string Name
keyPath
public string KeyPath
multiEntry
public bool MultiEntry
unique
public bool Unique
objectStore
public IDBObjectStore ObjectStore
Constructor
public IDBIndex(IDBObjectStore idbStore, string name, string keyPath, bool multiEntry = false, bool unique = false);
Methods
count()
public async Task<int> Count(string indexName);
public async Task<int> Count<TKey>(TKey key);
public async Task<int> Count<TKey>(IDBKeyRange<TKey> key);
get()
public async Task<TResult> Get<TKey, TResult>(TKey queryValue);
getAll()
public async Task<List<TResult>> GetAll<TResult>(int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey[] key);
getKey()
public async Task<TResult> GetKey<TKey, TResult>(TKey queryValue);
getAllKeys()
public async Task<List<TResult>> GetAllKeys<TResult>(int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey[] key);
Query
public async Task<List<TResult>> Query<TResult>(string filter, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, TKey key, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, IDBKeyRange<TKey> key, int? count = null, int? skip = null)
Advanced query functions
The filter expression is the body of a function that receives de parameter obj
than handle each record of ObjectStore.
The function must return an Object of type TResult, that will be included in the List<TResult>
result and can be one of the following options:
- the same object
- a new object
- an array of new objects (unwind)
- undefined (record is not included in result)
for example, return a list of objects that contains the world "per"
in property firstName
ordered using index lastName
.
List<Person> result = await theFactoryDb.Store("people").Index("lastName").Query<Person>(
"if (obj.firstName.toLowerCase().includes('per')) return obj;"
);
Demo
Check simple Blazor WASM PWA demo in Demos BlazorIndexedDbJsClientDemo
Using the library
requires
NET 5.0 or newer
1. Install NuGet package
Install-Package BlazorIndexedDbJs
or
dotnet add package BlazorIndexedDbJs
2. Refence to BlazorIndexedDb.js library
For blazor wasm, in wwwroot\index.html
...
<body>
...
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/BlazorIndexedDbJs/BlazorIndexedDb.js"></script>
</body>
For blazor server, in Pages/_Host.cshtml
...
<body>
...
<script src="_framework/blazor.server.js"></script>
<script src="_content/BlazorIndexedDbJs/BlazorIndexedDb.js"></script>
</body>
3. create a database definition
Although it is not mandatory, because ObjectStores and Indexes can be accessed by name, it is preferable to define the database schema using classes, this facilitates refactoring and detecting errors at compile time.
Without classes
var people = await theFactoryDb.Store("Employees").Index("firstName").GetAll<Person>();
With classes
var people = await theFactoryDb.Employees.FirstName.GetAll<Person>();
Example of schema using classes:
Data/TheFactoryDb.cs
using System.Collections.Generic;
using Microsoft.JSInterop;
using BlazorIndexedDbJs;
namespace BlazorIndexedDbJsClientDemo.Data
{
public class Employees: IDBObjectStore
{
public IDBIndex FirstName { get; }
public IDBIndex LastName { get; }
public IDBIndex FullName { get; }
public Employees(IDBDatabase database): base(database)
{
Name = "Employees";
KeyPath = "id";
AutoIncrement = true;
FirstName = new IDBIndex(this, "firstName", "firstName");
LastName = new IDBIndex(this, "lastName", "lastName");
FullName = new IDBIndex(this, "fullName", "firstName,lastName");
}
}
public class TheFactoryDb: IDBDatabase
{
public Employees Employees { get; }
public TheFactoryDb(IJSRuntime jsRuntime): base(jsRuntime)
{
Name = "TheFactory";
Version = 1;
Employees = new Employees(this);
}
}
}
4. Add a scoped service for each IDBDatabase
For blazor wasm, in program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<TheFactoryDb>();
await builder.Build().RunAsync();
}
}
For blazor server, in startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
services.AddScoped<TheFactoryDb>();
}
Examples
For the following examples we are going to assume that we have Person class which is defined as follows:
public class Person
{
public long? Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And the data store name is "Employees"
Accessing IDBDatabase
To use IndexedDB in a component or page, first inject the IDBDatabase instance.
@inject TheFactoryDb theFactoryDb
Open database
This will create the database if it not exists and will upgrade schema to new version if it is older.
await theFactoryDb.Open()
Getting all records from a store
var people = await theFactoryDb.Employees.GetAll<Person>();
Get one record by Id
var person = await theFactoryDb.Employees.Get<long, Person>(id);
Getting one record using an index
var person = await theFactoryDb.Employees.FirstName.Get<string, Person>("John");
Getting all records from an index
var people = await theFactoryDb.Employees.FirstName.GetAll<string, Person>("John");
Adding a record to an IDBObjectStore
var newPerson = new Person() {
FirstName = "John",
LastName = "Doe"
};
await theFactoryDb.Employees.Add(newPerson);
Updating a record
await theFactoryDb.Employees.Put<Person>(recordToUpdate)
Deleting a record
await theFactoryDb.Employees.Delete<int>(id)
Clear all records from a store
await theFactoryDb.Employees.Clear()
Deleting the database
await theFactoryDb.DeleteDb()