Examples
This page contains practical examples of using DexBee in real applications. Each example includes complete code and explanations.
Basic Todo App
A simple todo application demonstrating CRUD operations:
import { and, DexBee, eq } from 'dexbee-js'
// Define the schema
const todoSchema = {
version: 1,
tables: {
todos: {
schema: {
id: { type: 'number', required: true },
title: { type: 'string', required: true },
completed: { type: 'boolean', default: () => false },
createdAt: { type: 'date', default: () => new Date() },
category: { type: 'string', default: () => 'general' }
},
primaryKey: 'id',
autoIncrement: true,
indexes: [
{ name: 'completed_idx', keyPath: 'completed' },
{ name: 'category_idx', keyPath: 'category' },
{ name: 'created_idx', keyPath: 'createdAt' }
]
}
}
}
// Initialize database
const db = await DexBee.connect('todoapp', todoSchema)
// Todo operations
class TodoService {
// Create a new todo
async createTodo(title: string, category: string = 'general') {
return await db.table('todos').insert({
title,
category,
completed: false
})
}
// Get all todos
async getAllTodos() {
return await db.table('todos')
.orderBy('createdAt', 'desc')
.all()
}
// Get todos by status
async getTodosByStatus(completed: boolean) {
return await db.table('todos')
.where(eq('completed', completed))
.orderBy('createdAt', 'desc')
.all()
}
// Get todos by category
async getTodosByCategory(category: string) {
return await db.table('todos')
.where(eq('category', category))
.orderBy('createdAt', 'desc')
.all()
}
// Toggle todo completion
async toggleTodo(id: number) {
const todo = await db.table('todos').findById(id)
if (!todo)
throw new Error('Todo not found')
return await db.table('todos').update(id, {
completed: !todo.completed
})
}
// Delete todo
async deleteTodo(id: number) {
return await db.table('todos').delete(id)
}
// Get statistics
async getStats() {
const [total, completed, pending] = await Promise.all([
db.table('todos').count(),
db.table('todos').where(eq('completed', true)).count(),
db.table('todos').where(eq('completed', false)).count()
])
return { total, completed, pending }
}
}
User Management System
A more complex example with relationships and data validation:
import { and, DexBee, eq, gte, or } from 'dexbee-js'
const userSchema = {
version: 1,
tables: {
users: {
schema: {
id: { type: 'number', required: true },
username: { type: 'string', required: true, unique: true },
email: { type: 'string', required: true, unique: true },
firstName: { type: 'string', required: true },
lastName: { type: 'string', required: true },
age: { type: 'number', required: true },
role: { type: 'string', default: () => 'user' },
isActive: { type: 'boolean', default: () => true },
createdAt: { type: 'date', default: () => new Date() },
lastLoginAt: { type: 'date' }
},
primaryKey: 'id',
autoIncrement: true,
indexes: [
{ name: 'username_idx', keyPath: 'username', unique: true },
{ name: 'email_idx', keyPath: 'email', unique: true },
{ name: 'role_idx', keyPath: 'role' },
{ name: 'active_idx', keyPath: 'isActive' }
]
},
profiles: {
schema: {
id: { type: 'number', required: true },
userId: { type: 'number', required: true },
bio: { type: 'string' },
avatar: { type: 'string' },
website: { type: 'string' },
location: { type: 'string' },
preferences: { type: 'object', default: () => ({}) }
},
primaryKey: 'id',
autoIncrement: true,
indexes: [
{ name: 'user_idx', keyPath: 'userId', unique: true }
]
}
}
}
const db = await DexBee.connect('userapp', userSchema)
class UserService {
// Create user with profile
async createUser(userData: {
username: string
email: string
firstName: string
lastName: string
age: number
role?: string
}, profileData?: {
bio?: string
avatar?: string
website?: string
location?: string
}) {
return await db.withWriteTransaction(['users', 'profiles'], async (tx) => {
// Validate age
if (userData.age < 13) {
throw new Error('Users must be at least 13 years old')
}
// Create user
const user = await db.table('users').insert(userData)
// Create profile if provided
if (profileData) {
await db.table('profiles').insert({
userId: user.id,
...profileData
})
}
return user
})
}
// Get user with profile
async getUserWithProfile(userId: number) {
const [user, profile] = await Promise.all([
db.table('users').findById(userId),
db.table('profiles').where(eq('userId', userId)).first()
])
return user ? { ...user, profile } : null
}
// Search users
async searchUsers(query: {
role?: string
isActive?: boolean
minAge?: number
limit?: number
}) {
let userQuery = db.table('users')
const conditions = []
if (query.role) {
conditions.push(eq('role', query.role))
}
if (typeof query.isActive === 'boolean') {
conditions.push(eq('isActive', query.isActive))
}
if (query.minAge) {
conditions.push(gte('age', query.minAge))
}
if (conditions.length > 0) {
userQuery = userQuery.where(and(...conditions))
}
return await userQuery
.orderBy('createdAt', 'desc')
.limit(query.limit || 50)
.all()
}
// Update last login
async updateLastLogin(userId: number) {
return await db.table('users').update(userId, {
lastLoginAt: new Date()
})
}
// Get user statistics
async getUserStats() {
const [
totalUsers,
activeUsers,
adminCount,
recentUsers
] = await Promise.all([
db.table('users').count(),
db.table('users').where(eq('isActive', true)).count(),
db.table('users').where(eq('role', 'admin')).count(),
db.table('users')
.where(gte('createdAt', new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)))
.count()
])
return {
total: totalUsers,
active: activeUsers,
admins: adminCount,
recentSignups: recentUsers
}
}
}
E-commerce Product Catalog
Complex querying with categories, pricing, and inventory:
import { and, between, DexBee, eq, gte, inArray, lte, not, or } from 'dexbee-js'
const catalogSchema = {
version: 1,
tables: {
products: {
schema: {
id: { type: 'number', required: true },
name: { type: 'string', required: true },
description: { type: 'string' },
price: { type: 'number', required: true },
categoryId: { type: 'number', required: true },
sku: { type: 'string', required: true, unique: true },
inStock: { type: 'boolean', default: () => true },
stockCount: { type: 'number', default: () => 0 },
rating: { type: 'number', default: () => 0 },
reviewCount: { type: 'number', default: () => 0 },
tags: { type: 'array', default: () => [] },
createdAt: { type: 'date', default: () => new Date() }
},
primaryKey: 'id',
autoIncrement: true,
indexes: [
{ name: 'sku_idx', keyPath: 'sku', unique: true },
{ name: 'category_idx', keyPath: 'categoryId' },
{ name: 'price_idx', keyPath: 'price' },
{ name: 'stock_idx', keyPath: 'inStock' },
{ name: 'rating_idx', keyPath: 'rating' }
]
},
categories: {
schema: {
id: { type: 'number', required: true },
name: { type: 'string', required: true },
slug: { type: 'string', required: true, unique: true },
parentId: { type: 'number' },
description: { type: 'string' }
},
primaryKey: 'id',
autoIncrement: true,
indexes: [
{ name: 'slug_idx', keyPath: 'slug', unique: true },
{ name: 'parent_idx', keyPath: 'parentId' }
]
}
}
}
const db = await DexBee.connect('catalog', catalogSchema)
class ProductService {
// Search products with filters
async searchProducts(filters: {
categoryId?: number
priceMin?: number
priceMax?: number
inStock?: boolean
minRating?: number
tags?: string[]
sortBy?: 'price' | 'rating' | 'created'
sortOrder?: 'asc' | 'desc'
limit?: number
offset?: number
}) {
let query = db.table('products')
const conditions = []
// Category filter
if (filters.categoryId) {
conditions.push(eq('categoryId', filters.categoryId))
}
// Price range
if (filters.priceMin && filters.priceMax) {
conditions.push(between('price', filters.priceMin, filters.priceMax))
}
else if (filters.priceMin) {
conditions.push(gte('price', filters.priceMin))
}
else if (filters.priceMax) {
conditions.push(lte('price', filters.priceMax))
}
// Stock filter
if (typeof filters.inStock === 'boolean') {
conditions.push(eq('inStock', filters.inStock))
}
// Rating filter
if (filters.minRating) {
conditions.push(gte('rating', filters.minRating))
}
// Apply conditions
if (conditions.length > 0) {
query = query.where(and(...conditions))
}
// Sorting
const sortBy = filters.sortBy || 'created'
const sortOrder = filters.sortOrder || 'desc'
if (sortBy === 'created') {
query = query.orderBy('createdAt', sortOrder)
}
else {
query = query.orderBy(sortBy, sortOrder)
}
// Pagination
if (filters.offset) {
query = query.offset(filters.offset)
}
const limit = filters.limit || 20
const products = await query.limit(limit).all()
// Filter by tags (IndexedDB doesn't support array contains)
let filteredProducts = products
if (filters.tags && filters.tags.length > 0) {
filteredProducts = products.filter(product =>
filters.tags!.some(tag => product.tags.includes(tag))
)
}
return filteredProducts
}
// Get featured products
async getFeaturedProducts(limit: number = 10) {
return await db.table('products')
.where(and(
eq('inStock', true),
gte('rating', 4.0)
))
.orderBy('rating', 'desc')
.limit(limit)
.all()
}
// Get products by category with hierarchy
async getProductsByCategory(categorySlug: string) {
// Get category
const category = await db.table('categories')
.where(eq('slug', categorySlug))
.first()
if (!category)
return []
// Get child categories
const childCategories = await db.table('categories')
.where(eq('parentId', category.id))
.all()
const categoryIds = [category.id, ...childCategories.map(c => c.id)]
// Get products from all relevant categories
return await db.table('products')
.where(and(
inArray('categoryId', categoryIds),
eq('inStock', true)
))
.orderBy('rating', 'desc')
.all()
}
// Update inventory
async updateStock(productId: number, quantity: number) {
const product = await db.table('products').findById(productId)
if (!product)
throw new Error('Product not found')
const newStockCount = Math.max(0, product.stockCount + quantity)
return await db.table('products').update(productId, {
stockCount: newStockCount,
inStock: newStockCount > 0
})
}
// Get low stock products
async getLowStockProducts(threshold: number = 10) {
return await db.table('products')
.where(and(
eq('inStock', true),
lte('stockCount', threshold)
))
.orderBy('stockCount', 'asc')
.all()
}
}
Data Analytics Dashboard
Aggregation and reporting examples:
class AnalyticsService {
// Sales analytics
async getSalesMetrics(dateRange?: { start: Date, end: Date }) {
let query = db.table('orders')
if (dateRange) {
query = query.where(between('createdAt', dateRange.start, dateRange.end))
}
const orders = await query.all()
// Calculate metrics
const totalSales = orders.reduce((sum, order) => sum + order.total, 0)
const averageOrderValue = totalSales / orders.length || 0
const totalOrders = orders.length
// Group by status
const ordersByStatus = orders.reduce((acc, order) => {
acc[order.status] = (acc[order.status] || 0) + 1
return acc
}, {} as Record<string, number>)
return {
totalSales,
averageOrderValue,
totalOrders,
ordersByStatus
}
}
// User engagement metrics
async getUserEngagement() {
const [
totalUsers,
activeUsers,
newUsers
] = await Promise.all([
db.table('users').count(),
db.table('users')
.where(gte('lastLoginAt', new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)))
.count(),
db.table('users')
.where(gte('createdAt', new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)))
.count()
])
return {
total: totalUsers,
active: activeUsers,
new: newUsers,
engagementRate: totalUsers > 0 ? (activeUsers / totalUsers) * 100 : 0
}
}
// Product performance
async getTopProducts(limit: number = 10) {
return await db.table('products')
.where(eq('inStock', true))
.orderBy('reviewCount', 'desc')
.limit(limit)
.select('id', 'name', 'rating', 'reviewCount', 'price')
.all()
}
}
Migration Example
Additive schema evolution with dry-run validation:
import { DexBee } from 'dexbee-js'
import { withMigrations } from 'dexbee-js/migrations'
// Version 1 schema
const schemaV1 = {
version: 1,
tables: {
users: {
schema: {
id: { type: 'number', required: true },
name: { type: 'string', required: true },
email: { type: 'string', required: true }
},
primaryKey: 'id',
autoIncrement: true
}
}
}
// Version 2 schema with new fields (additive only)
const schemaV2 = {
version: 2,
tables: {
users: {
schema: {
id: { type: 'number', required: true },
name: { type: 'string', required: true },
email: { type: 'string', required: true, unique: true },
// NEW: Added fields with defaults
createdAt: { type: 'date', default: () => new Date() },
isActive: { type: 'boolean', default: () => true }
},
primaryKey: 'id',
autoIncrement: true,
indexes: [
{ name: 'email_idx', keyPath: 'email', unique: true }
]
}
}
}
// Connect and add migration capabilities
const db = await DexBee.connect('myapp', schemaV1)
const migratable = withMigrations(db)
// Preview migration first (dry run)
const dryRun = await migratable.dryRunMigration(schemaV2)
console.log('Operations:', dryRun.operations)
console.log('Warnings:', dryRun.warnings)
// Apply if safe
if (dryRun.isValid && dryRun.warnings.length === 0) {
const result = await migratable.migrate(schemaV2, {
validateEachStep: true
})
if (result.success) {
console.log('Migration completed successfully')
}
}
else {
console.error('Migration has warnings:', dryRun.warnings)
}
For more migration patterns and best practices, see the Migrations Guide.
These examples demonstrate various patterns and use cases for DexBee. You can adapt them to your specific needs and combine different techniques for more complex applications.
On This Page