TypeScript ORM wrapper for IndexedDB with Zod runtime validation, built on top of Dexie.js
Full type safety with generics and IntelliSense support. Write type-safe database operations with complete IDE assistance.
Runtime validation with Zod schemas. Ensure data integrity with powerful validation rules and clear error messages.
Define entities with a simple `defineEntity()` - clean, functional approach to entity definition.
All Dexie.js operations supported. Leverage the full power of Dexie with additional ORM capabilities.
Simple and intuitive API design. Focus on your business logic, not database complexity.
Support for entity relationships and powerful aggregation operations. Build complex queries with ease.
Comprehensive demo applications showcasing all features across different frameworks:
Important: This library includes Dexie.js internally. To ensure that liveQuery works properly, you need to configure Vite to dedupe Dexie.
// In your vite.config.ts, add:
import { defineConfig } from 'vite';
export default defineConfig({
resolve: {
dedupe: ['dexie'],
},
// ... other config
});
// This ensures liveQuery works correctly
// Without dedupe, liveQuery may not function properlyFoundation: This library is built on top of Dexie.js - a powerful wrapper for IndexedDB. All Dexie.js methods and features are available through the db object.
// All Dexie.js methods work with our library
const db = await Database.createDatabase({...});
// Direct Dexie.js operations
await db.transaction('rw', db.users, async () => {
await db.users.add({ name: 'John', email: 'john@example.com' });
});
// Dexie.js query methods
const users = await db.users
.where('age')
.above(18)
.and(user => user.name.startsWith('J'))
.toArray();
// Dexie.js liveQuery (reactive queries)
import { liveQuery } from 'dexie';
const observableUsers = liveQuery(() =>
db.users.where('active').equals(true).toArray()
);Benefits: You get all the power of Dexie.js plus our ORM features like entity validation, relations, and cloud sync.
To enable cloud synchronization, you need to complete several setup steps:
// 1. Install Dexie Cloud addon
npm install dexie-cloud-addon
// 2. Configure your database with cloud sync
const db = await Database.createDatabase({
name: 'MyAppDB',
version: 1,
entities: [User, Post],
cloudSync: {
appId: 'your-app-id',
serverUrl: 'https://your-server.com',
// ... other cloud config
}
});
// 3. Enable cloud sync after database creation
await db.enableCloudSync();Critical: When searching by any field, you MUST create an index on that field. Queries without proper indexes will fail.
When changing entity schemas, you need to handle migrations properly. The library provides two strategies for schema changes.
// Strategy 1: Selective reset (recommended)
const db = await Database.createDatabase({
name: 'MyAppDB',
version: 2, // Increment version
entities: [User, Post],
onSchemaChangeStrategy: 'selective' // Only affected tables are reset
});
// Strategy 2: Full reset (use with caution)
onSchemaChangeStrategy: 'all' // All data is lostFor queries involving multiple fields, you need compound indexes. Single field indexes won't work for multi-field queries.
Use Dexie's liveQuery for reactive data that automatically updates when the database changes. Works with all major frameworks:
import { liveQuery } from 'dexie';
import { useObservable } from 'dexie-react-hooks';
import { Database } from '@indexeddb-orm/idb-orm';
import { User } from './entities/User';
function UserList() {
const users = useObservable(
liveQuery(() => {
const userRepo = db.getRepository(User);
return userRepo.where('active').equals(true).toArray();
})
);
return (
{users?.map(user => (
{user.name}
))}
);
}import { liveQuery } from 'dexie';
import { ref, onMounted, onUnmounted } from 'vue';
import { Database } from '@indexeddb-orm/idb-orm';
import { User } from './entities/User';
export default {
setup() {
const users = ref([]);
let subscription;
onMounted(() => {
subscription = liveQuery(() => {
const userRepo = db.getRepository(User);
return userRepo.where('active').equals(true).toArray();
}).subscribe(result => {
users.value = result;
});
});
onUnmounted(() => {
subscription?.unsubscribe();
});
return { users };
}
}; import { liveQuery } from 'dexie';
import { onMount, onDestroy } from 'svelte';
import { Database } from '@indexeddb-orm/idb-orm';
import { User } from './entities/User';
let users: User[] = [];
let subscription;
onMount(() => {
subscription = liveQuery(() => {
const userRepo = db.getRepository(User);
return userRepo.where('active').equals(true).toArray();
}).subscribe(result => {
users = result;
});
});
onDestroy(() => {
subscription?.unsubscribe();
});import { liveQuery } from 'dexie';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { Database } from '@indexeddb-orm/idb-orm';
import { User } from './entities/User';
@Component({
selector: 'app-user-list',
template: '{{user.name}}'
})
export class UserListComponent implements OnInit, OnDestroy {
users: User[] = [];
private subscription?: Subscription;
ngOnInit() {
this.subscription = liveQuery(() => {
const userRepo = db.getRepository(User);
return userRepo.where('active').equals(true).toArray();
}).subscribe(result => {
this.users = result;
});
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
}Complete reference of all methods with JSDoc documentation from the library.
Main database class extending Dexie with ORM capabilities.
Base class for all entities with validation capabilities.
Schema management for entities.
Service for performing aggregation operations on entity data.
Service for loading and saving entity relations.
Service for building IndexedDB schemas from entities.
Service for cloud synchronization functionalities.
Service for managing database migrations.
Define entity metadata without decorators. Registers table name, schema, columns, indexes and relations for a given class so the ORM can work without TypeScript decorators.
Create a new entity instance and validate it in a single call. This helper instantiates the provided entity class, assigns the given partial data, and immediately validates it using the entity's `init` method (which calls `validateOrThrow` under the hood).
A fully initialized and validated entity instance
ValidationError if validation fails according to the entity schema
Error thrown when entity validation fails.
Only resets tables that have schema changes, preserving data in unchanged tables.
Resets the entire database when any schema change is detected.
Ready to build powerful IndexedDB applications with full TypeScript support?
Created by Maciej Radom | MIT License