fast-idb
v0.1.2
Published
Typed IndexedDB CRUD store factory with optional React/Zustand integration
Maintainers
Readme
fast-idb
Ultra-simple, typed IndexedDB CRUD. Just collection<T>('name') and go.
Optional React + Zustand integration included.
Install
bun add fast-idb
# peer dep (optional — only if you use fast-idb/react):
# zustand >= 4.0Quick Start
import { collection } from 'fast-idb'
interface Todo {
id: string
title: string
done: boolean
createdAt: string
updatedAt: string
}
const todos = collection<Todo>('todos')
// Create — id + timestamps are auto-generated
await todos.create({ title: 'Buy milk', done: false })
// Read
await todos.get('some-id')
await todos.getAll()
await todos.getAll({ direction: 'desc', limit: 10 })
// Update — merges fields, updates updatedAt
await todos.update('some-id', { done: true })
// Delete
await todos.delete('some-id')
// Bulk
await todos.bulkCreate([{ title: 'A', done: false }, { title: 'B', done: true }])
await todos.bulkDelete(['id-1', 'id-2'])
// Filter / Find
await todos.filter(t => t.done === true)
await todos.find(t => t.title.includes('milk'))
// Count / Clear
await todos.count()
await todos.clear()That's it. No database config, no version management, no setup. The library handles everything internally:
- Single shared IndexedDB database
- Object stores created on demand
- Auto-generated IDs (timestamp + random)
- Auto-managed
createdAt/updatedAttimestamps
With React + Zustand
// db.ts
import { collection } from 'fast-idb'
import { createCrudStore } from 'fast-idb/react'
import { toast } from 'sonner'
interface Todo {
id: string
title: string
done: boolean
createdAt: string
updatedAt: string
}
const todos = collection<Todo>('todos')
export const useTodos = createCrudStore(todos, {
sortDirection: 'desc',
labels: { entity: 'Todo' },
toast: (type, title, desc) => toast[type](title, { description: desc }),
})// TodoList.tsx
import { useEffect } from 'react'
import { useTodos } from './db'
export function TodoList() {
const { items, isLoading, fetchAll, create, remove } = useTodos()
useEffect(() => { fetchAll() }, [])
if (isLoading) return <p>Loading...</p>
return (
<div>
<button onClick={() => create({ title: 'New todo', done: false })}>
Add Todo
</button>
<ul>
{items.map(t => (
<li key={t.id}>
{t.title}
<button onClick={() => remove(t.id)}>x</button>
</li>
))}
</ul>
</div>
)
}Collection API
| Method | Description |
|---|---|
| get(key) | Get one record |
| getAll(options?) | Get all, with optional direction/limit/offset |
| count() | Count records |
| create(data) | Create (auto ID + timestamps) |
| update(key, data) | Partial merge update |
| put(item) | Full upsert (no merge) |
| delete(key) | Delete by key |
| clear() | Delete all records |
| bulkCreate(items) | Batch create in one transaction |
| bulkDelete(keys) | Batch delete in one transaction |
| forEach(cb, options?) | Cursor iteration |
| find(predicate) | First match |
| filter(predicate) | All matches |
Query Options
await todos.getAll({ direction: 'desc', limit: 10, offset: 5 })| Option | Type | Default | Description |
|---|---|---|---|
| direction | 'asc' \| 'desc' | 'asc' | Sort order |
| limit | number | - | Max results |
| offset | number | 0 | Skip N results |
Zustand Store API (fast-idb/react)
| Property/Method | Description |
|---|---|
| items | Current array of records |
| isLoading | Loading state |
| hasFetched | True after first fetch |
| error | Error message or null |
| fetchAll() | Load all from DB |
| create(data) | Create + prepend to items |
| update(key, data) | Update + sync items |
| remove(key) | Delete + remove from items |
| getOne(key) | Read single item |
| search(predicate, query?) | Client-side filter |
| clearSearch() | Reset to full list |
| openDialog(name, item?) | Open named dialog |
| closeDialog(name) | Close named dialog |
| selectedItem | Item selected via openDialog |
| refresh() | Re-fetch everything |
License
MIT
