ai-database
v2.0.2
Published
AI-powered database interface primitives with mdxld conventions
Maintainers
Readme
ai-database
Your data, flowing like conversation.
import { DB } from 'ai-database'
const { db } = DB({
Lead: { name: 'string', company: 'Company.leads' },
Company: { name: 'string' }
})
// Chain without await
const leads = db.Lead.list()
const qualified = await leads.filter(l => l.score > 80)
// Batch relationship loading
const enriched = await leads.map(lead => ({
name: lead.name,
company: lead.company, // Batch loaded!
}))Promise Pipelining
Chain database operations without await:
const leads = db.Lead.list()
const topLeads = leads.filter(l => l.score > 80)
const names = topLeads.map(l => l.name)
// Only await when you need the result
const result = await namesBatch Relationship Loading
Eliminate N+1 queries automatically:
// Old way - N+1 queries
const leads = await db.Lead.list()
for (const lead of leads) {
const company = await db.Company.get(lead.companyId) // N queries!
}
// New way - batch loaded
const enriched = await db.Lead.list().map(lead => ({
lead,
company: lead.company, // All companies loaded in ONE query
}))Natural Language Queries
Ask your database questions:
const results = await db.Lead`who closed deals this month?`
const pending = await db.Order`what's stuck in processing?`Real-World Examples
Sales Pipeline
const { db } = DB({
Lead: {
name: 'string',
email: 'string',
score: 'number',
company: 'Company.leads',
},
Company: {
name: 'string',
industry: 'string',
}
})
// Find high-value leads with their companies
const qualified = await db.Lead.list()
.filter(lead => lead.score > 80)
.map(lead => ({
lead,
company: lead.company,
}))
// Ask questions naturally
const results = await db.Lead`who hasn't responded in 2 weeks?`Customer Success
const { db } = DB({
Customer: {
name: 'string',
healthScore: 'number',
mrr: 'number',
csm: 'User.customers',
},
User: { name: 'string' }
})
// At-risk customers with their CSMs
const atRisk = await db.Customer.list()
.filter(c => c.healthScore < 50)
.map(c => ({
customer: c,
csm: c.csm,
mrr: c.mrr,
}))Order Management
const { db } = DB({
Order: {
status: 'string',
total: 'number',
customer: 'Customer.orders',
items: ['OrderItem.order'],
},
OrderItem: { product: 'string', quantity: 'number' },
Customer: { name: 'string' }
})
// Pending orders with all details
const pending = await db.Order
.find({ status: 'pending' })
.map(order => ({
order,
customer: order.customer,
items: order.items,
}))Schema Definition
Define once, get typed operations everywhere:
const { db, events, actions, nouns, verbs } = DB({
Post: {
title: 'string',
content: 'markdown',
author: 'Author.posts', // Creates both directions
},
Author: {
name: 'string',
// posts: Post[] auto-created from backref
}
})Field Types
| Type | Description |
|------|-------------|
| string | Text |
| number | Numeric |
| boolean | True/false |
| date | Date only |
| datetime | Date and time |
| markdown | Rich text |
| json | Structured data |
| url | URL string |
Relationships
// One-to-many: Post has one Author, Author has many Posts
Post: { author: 'Author.posts' }
// Many-to-many: Post has many Tags, Tag has many Posts
Post: { tags: ['Tag.posts'] }CRUD Operations
All operations return DBPromise for chaining:
// Read
const lead = await db.Lead.get('lead-123')
const leads = await db.Lead.list()
const first = await db.Lead.first()
const found = await db.Lead.find({ status: 'active' })
// Search
const results = await db.Lead.search('enterprise SaaS')
// Write (returns regular Promise)
const lead = await db.Lead.create({ name: 'Acme Corp' })
await db.Lead.update(lead.$id, { score: 90 })
await db.Lead.delete(lead.$id)Chainable Methods
db.Lead.list()
.filter(l => l.score > 50)
.sort((a, b) => b.score - a.score)
.limit(10)
.map(l => ({ name: l.name, score: l.score }))Events
React to changes in real-time:
events.on('Lead.created', event => {
notifySlack(`New lead: ${event.data.name}`)
})
events.on('*.updated', event => {
logChange(event)
})forEach - Large-Scale Processing
Process thousands of items with concurrency, progress tracking, and error handling:
// Simple iteration
await db.Lead.forEach(lead => {
console.log(lead.name)
})
// With AI and concurrency
const result = await db.Lead.forEach(async lead => {
const analysis = await ai`analyze ${lead}`
await db.Lead.update(lead.$id, { analysis })
}, {
concurrency: 10,
onProgress: p => console.log(`${p.completed}/${p.total} (${p.rate.toFixed(1)}/s)`),
})
// With error handling and retries
await db.Order.forEach(async order => {
await sendInvoice(order)
}, {
concurrency: 5,
maxRetries: 3,
retryDelay: attempt => 1000 * Math.pow(2, attempt), // Exponential backoff
onError: (err, order) => err.code === 'RATE_LIMIT' ? 'retry' : 'continue',
})
console.log(`Completed: ${result.completed}, Failed: ${result.failed}`)forEach Options
| Option | Type | Description |
|--------|------|-------------|
| concurrency | number | Max parallel operations (default: 1) |
| maxRetries | number | Retries per item (default: 0) |
| retryDelay | number \| fn | Delay between retries |
| onProgress | fn | Progress callback |
| onError | 'continue' \| 'retry' \| 'skip' \| 'stop' \| fn | Error handling |
| timeout | number | Timeout per item in ms |
| persist | boolean \| string | Enable durability (string = custom action name) |
| resume | string | Resume from action ID |
Durable forEach
Persist progress to survive crashes:
// Enable persistence - auto-names action as "Lead.forEach"
const result = await db.Lead.forEach(processLead, {
concurrency: 10,
persist: true,
})
console.log(`Action ID: ${result.actionId}`)Custom action name:
await db.Lead.forEach(processLead, {
persist: 'analyze-leads', // Custom action name
})Resume after a crash:
await db.Lead.forEach(processLead, {
resume: 'action-123', // Skips already-processed items
})Actions
Track long-running operations:
const action = await actions.create({
type: 'import-leads',
data: { file: 'leads.csv' },
total: 1000,
})
await actions.update(action.id, { progress: 500 })
await actions.update(action.id, { status: 'completed' })Installation
pnpm add ai-databaseConfiguration
DATABASE_URL=./content # filesystem (default)
DATABASE_URL=sqlite://./data # SQLite
DATABASE_URL=:memory: # in-memoryDocumentation
Document Database Interface
In addition to the schema-first graph model, ai-database also exports environment-agnostic types for document-based storage (MDX files with frontmatter). These types are used by @mdxdb/* adapters and work in any JavaScript runtime (Node.js, Bun, Deno, Workers, Browser).
import type {
DocumentDatabase,
DocListOptions,
DocSearchOptions,
Document,
} from 'ai-database'
// The DocumentDatabase interface
interface DocumentDatabase<TData> {
list(options?: DocListOptions): Promise<DocListResult<TData>>
search(options: DocSearchOptions): Promise<DocSearchResult<TData>>
get(id: string, options?: DocGetOptions): Promise<Document<TData> | null>
set(id: string, doc: Document<TData>, options?: DocSetOptions): Promise<DocSetResult>
delete(id: string, options?: DocDeleteOptions): Promise<DocDeleteResult>
close?(): Promise<void>
}Document Types
| Type | Description |
|------|-------------|
| Document<TData> | MDX document with id, type, context, data, and content |
| DocumentDatabase<TData> | Interface for document storage adapters |
| DocListOptions | Options for listing documents (limit, offset, sortBy, type, prefix) |
| DocListResult<TData> | List result with documents, total, hasMore |
| DocSearchOptions | Search options (query, fields, semantic) |
| DocSearchResult<TData> | Search result with scores |
| DocGetOptions | Get options (includeAst, includeCode) |
| DocSetOptions | Set options (createOnly, updateOnly, version) |
| DocSetResult | Set result (id, version, created) |
| DocDeleteOptions | Delete options (soft, version) |
| DocDeleteResult | Delete result (id, deleted) |
View Types
For bi-directional relationship rendering:
| Type | Description |
|------|-------------|
| ViewManager | Interface for managing views |
| ViewDocument | View template definition |
| ViewContext | Context for rendering a view |
| ViewRenderResult | Rendered markdown and entities |
| ViewSyncResult | Mutations from extracting edited markdown |
| DocumentDatabaseWithViews | Database with view support |
Usage with @mdxdb adapters
// Filesystem adapter
import { createFsDatabase } from '@mdxdb/fs'
const db = createFsDatabase({ root: './content' })
// API adapter
import { createApiDatabase } from '@mdxdb/api'
const db = createApiDatabase({ baseUrl: 'https://api.example.com' })
// SQLite adapter
import { createSqliteDatabase } from '@mdxdb/sqlite'
const db = createSqliteDatabase({ path: './data.db' })
// Same DocumentDatabase interface regardless of backend
const doc = await db.get('posts/hello-world')
await db.set('posts/new', { data: { title: 'New Post' }, content: '# Hello' })Related
- ai-functions - AI-powered functions
- ai-workflows - Event-driven workflows
- @mdxdb/fs - Filesystem adapter
- @mdxdb/sqlite - SQLite adapter
- @mdxdb/api - HTTP API client
