Examples
Filter by tag:
| Summary | Tags | Source |
|---|---|---|
| Drizzle table-to-zod | Data Architecture | |
| Zod table-to-zod2 | Data Architecture | |
| modelFromZod factory and ExtendedModel usage model-from-zod-factory | Data Architecture | |
| Type-safe reactive query bridge utility reactive-query-utility | Data Architecture | |
| Type-safe service with Drizzle and Zod typed-service | Data Architecture | |
| ObjectMap store with surgical reconciliation map-based-store | Data Architecture | |
| op-sqlite configuration with extensions op-sqlite-config | Data Architecture | |
| Mobile migration bundling and runtime execution mobile-migration-setup | Data Architecture | |
| Draft key encoding examples draft-key-format | Draft Store | |
| Draft schema derived from domain insert schema derived-draft-schema | Draft Store | |
| Domain functions with model-first convention domain-function-convention | Draft Store | |
| Generic Draft model with key encoding draft-model-definition | Draft Store | |
| DraftStore with debounced KV persistence draft-store-implementation | Draft Store | |
| Domain model with applyDraft write surface apply-draft-action | Draft Store | |
| Editor component binding to draft editor-component | Draft Store | |
| Patch-to-SQL translator function patch-translator | Patch Persistence | |
| Action tracking middleware for grouped SQL persistence action-batching-middleware | Patch Persistence | |
| Merged patch flush with grouped UPDATE statements grouped-flush | Patch Persistence | |
| Undo/redo with inverse patches and automatic SQL rollback undo-redo-wiring | Patch Persistence | |
| Using Keystone's built-in undoMiddleware builtin-undo-manager | Patch Persistence | |
| Service with injected AppDatabase injectable-service | Testing Strategy | |
| In-memory test database factory with pushSchema test-db-factory | Testing Strategy | |
| Per-test database isolation test-harness | Testing Strategy | |
| Device test for reactive query firing device-test-reactive | Testing Strategy |
No examples match the selected tags.
Drizzle
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'import { createSelectSchema, createInsertSchema, createUpdateSchema } from 'drizzle-orm/zod'
// 1. Single source of truthexport const todos = sqliteTable('todos', { id: text('id').primaryKey(), title: text('title').notNull(), description: text('description'), completedAt: integer('completed_at'), listId: text('list_id').notNull(), createdAt: integer('created_at').notNull(),})
// 2. Zod schemas derived for freeexport const TodoSelectSchema = createSelectSchema(todos, { title: (s) => s.min(1).max(200),})export const TodoInsertSchema = createInsertSchema(todos)export const TodoUpdateSchema = createUpdateSchema(todos)
// 3. TS types flow through automaticallyexport type TodoRow = typeof todos.$inferSelectexport type TodoInsert = typeof todos.$inferInsert Zod
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'import { createSelectSchema, createInsertSchema, createUpdateSchema } from 'drizzle-orm/zod'
// 2. Zod schemas derived for freeexport const TodoSelectSchema = createSelectSchema(todos, { title: (s) => s.min(1).max(200),})export const TodoInsertSchema = createInsertSchema(todos)export const TodoUpdateSchema = createUpdateSchema(todos)
// 3. TS types flow through automaticallyexport type TodoRow = typeof todos.$inferSelectexport type TodoInsert = typeof todos.$inferInsert modelFromZod factory and ExtendedModel usage
import { z } from 'zod'import { Model, prop, tProp, types, ExtendedModel } from 'mobx-keystone'
function zodToKeystoneType(zodType: z.ZodTypeAny): any { if (zodType instanceof z.ZodString) return types.string if (zodType instanceof z.ZodNumber) return types.number if (zodType instanceof z.ZodBoolean) return types.boolean if (zodType instanceof z.ZodOptional) return types.maybe(zodToKeystoneType(zodType.unwrap())) if (zodType instanceof z.ZodNullable) return types.maybeNull(zodToKeystoneType(zodType.unwrap())) if (zodType instanceof z.ZodArray) return types.array(zodToKeystoneType(zodType.element)) throw new Error(`Unsupported Zod type: ${zodType.constructor.name}`)}
function zodToKeystoneProp(zodType: z.ZodTypeAny) { if (zodType instanceof z.ZodDefault) return tProp(zodToKeystoneType(zodType.removeDefault())) .withDefault(zodType._def.defaultValue()) return tProp(zodToKeystoneType(zodType))}
function modelFromZod<T extends z.ZodObject<any>>(modelType: string, schema: T) { const keystoneProps = Object.fromEntries( Object.entries(schema.shape).map(([key, zodType]) => [ key, zodToKeystoneProp(zodType as z.ZodTypeAny), ]) )
@model(modelType) class DerivedModel extends Model(keystoneProps as any) { static validate(data: unknown): z.infer<T> { return schema.parse(data) } static fromRaw(data: unknown) { return new DerivedModel(DerivedModel.validate(data)) } } return DerivedModel}
// Usage: extend for domain behaviourconst BaseTodo = modelFromZod('BaseTodo', TodoSelectSchema)
@model('Todo')class Todo extends ExtendedModel(BaseTodo, {}) { @computed get isCompleted() { return this.completedAt !== null }} Type-safe reactive query bridge utility
import { QueryBuilder } from 'drizzle-orm/sqlite-core'import { type SQLiteTable } from 'drizzle-orm/sqlite-core'import { type z } from 'zod'
const qb = new QueryBuilder()
// Extract table names from Drizzle table objectsfunction tableNames(...tables: SQLiteTable[]): string[] { return tables.map(t => t[Symbol.for('drizzle:Name')])}
type ReactiveQueryOptions<TSchema extends z.ZodTypeAny> = { query: ReturnType<typeof qb.select> tables: string[] schema: TSchema callback: (rows: z.infer<TSchema>[]) => void}
function reactiveQuery<TSchema extends z.ZodTypeAny>({ query, tables, schema, callback,}: ReactiveQueryOptions<TSchema>) { const { sql, params } = query.toSQL()
return opsqliteDb.reactiveExecute({ query: sql, arguments: params, tables, callback: (rows: unknown[]) => { const parsed = z.array(schema).parse(rows) callback(parsed) }, })}
// Usage — fully type-safe, no raw SQL stringsconst unsub = reactiveQuery({ query: qb .select({ id: todos.id, title: todos.title, completedAt: todos.completedAt }) .from(todos) .where(eq(todos.listId, listId)) .orderBy(desc(todos.createdAt)), tables: tableNames(todos), schema: TodoSelectSchema, callback: (rows) => { // rows: z.infer<typeof TodoSelectSchema>[] — fully typed applySnapshot(store.todos, rows) },}) Type-safe service with Drizzle and Zod
type AppDatabase = ReturnType<typeof drizzle<typeof schema>>
class TodoService { constructor(private db: AppDatabase) {}
async create(input: z.infer<typeof TodoInsertSchema>) { await this.db.insert(todos).values(input) }
async update(id: string, input: z.infer<typeof TodoUpdateSchema>) { await this.db.update(todos).set(input).where(eq(todos.id, id)) }
async complete(id: string) { const todo = await this.db.select().from(todos) .where(eq(todos.id, id)).then(rows => rows[0])
if (todo.blockedBy) throw new Error('Cannot complete a blocked todo')
await this.db.update(todos) .set({ completedAt: Date.now() }) .where(eq(todos.id, id)) }} ObjectMap store with surgical reconciliation
import { Model, prop, objectMap, ObjectMap, modelAction } from 'mobx-keystone'import { runInAction } from 'mobx'
@model('TodoStore')class TodoStore extends Model({ todosMap: prop<ObjectMap<Todo>>(() => objectMap()),}) { @computed get todos() { return Array.from(this.todosMap.values()) }
@modelAction addTodo(todo: Todo) { this.todosMap.set(todo.id, todo) }
@modelAction removeTodo(id: string) { this.todosMap.delete(id) }}
// Surgical reconciliation in reactive callbackfunction reconcileTodos(store: TodoStore, rows: TodoRow[]) { runInAction(() => { const rowMap = new Map(rows.map(r => [r.id, r]))
// Remove deleted for (const [id] of store.todosMap.items()) { if (!rowMap.has(id)) store.removeTodo(id) }
// Add new, update existing for (const [id, row] of rowMap) { const existing = store.todosMap.get(id) if (existing) { applySnapshot(existing, row) // per-instance reconcile } else { store.addTodo(Todo.fromRaw(row)) } } })} op-sqlite configuration with extensions
{ "op-sqlite": { "fts5": true, "sqliteVec": true, "rtree": true, "performanceMode": true }} Mobile migration bundling and runtime execution
// babel.config.js — inline .sql files into the JS bundlemodule.exports = { presets: ['module:@react-native/babel-preset'], plugins: [['inline-import', { extensions: ['.sql'] }]],}
// App.tsx — run migrations before renderingimport { useMigrations } from 'drizzle-orm/op-sqlite/migrator'import migrations from './drizzle/migrations'
const opsqliteDb = open({ name: 'app.db' })const db = drizzle(opsqliteDb, { schema })
export default function App() { const { success, error } = useMigrations(db, migrations) if (error) return <MigrationErrorScreen error={error} /> if (!success) return <SplashScreen /> return <RootNavigator />} Draft key encoding examples
todo:abc-123 -> editing existing todo with id abc-123
todo:new:def-456 -> creating a new todo (draft id def-456)
project:xyz-789 -> editing existing project with id xyz-789
Draft schema derived from domain insert schema
export const TodoDraftSchema = TodoInsertSchema .omit({ id: true, createdAt: true }) // generated fields .extend({ refId: z.string().nullable(), // draft-specific metadata }) .partial() // all fields optional while editing .extend({ title: z.string().default(''), // override: empty string, not missing })
const TodoDraft = modelFromZod('TodoDraft', TodoDraftSchema) Domain functions with model-first convention
// Domain functions — pure, no class needed// First argument is always the domain object (or store for creation)
function commitTodoEdit(todo: Todo, draft: TodoDraft): void { if (!draft.isValid) throw new Error('Title required') if (todo.completedAt && !draft.completedAt) { throw new Error('Cannot un-complete a todo') } // single @modelAction — one patch group, one undo entry, one SQL transaction todo.applyDraft(draft)}
function commitTodoCreate(store: TodoStore, draft: TodoDraft): void { if (!draft.isValid) throw new Error('Title required') store.addTodo(Todo.fromDraft(draft))}
// Namespace for discoverabilityconst TodoDomain = { edit: (todo: Todo, draft: TodoDraft) => { /* ... */ }, complete: (todo: Todo) => { /* ... */ }, block: (todo: Todo, blockedBy: Todo) => { /* ... */ },}
const TodoStoreDomain = { create: (store: TodoStore, draft: TodoDraft) => { /* ... */ }, reorder: (store: TodoStore, ids: string[]) => { /* ... */ },} Generic Draft model with key encoding
@model('Draft')class Draft extends Model({ key: prop<string>(), // "todo:123" | "todo:new:abc" type: prop<string>(), // "todo" refId: prop<string | null>(null), // null = new entity data: prop<Record<string, unknown>>(), createdAt: prop<number>(), updatedAt: prop<number>(),}) { @computed get isNew() { return this.refId === null }} DraftStore with debounced KV persistence
@model('DraftStore')class DraftStore extends Model({ drafts: prop<Draft[]>(() => []),}) { @modelAction upsertDraft(key: string, data: Record<string, unknown>) { const existing = this.drafts.find(d => d.key === key) if (existing) { existing.data = data existing.updatedAt = Date.now() } else { const [type, maybeNew, ...rest] = key.split(':') this.drafts.push(new Draft({ key, type, refId: maybeNew === 'new' ? null : maybeNew, data, createdAt: Date.now(), updatedAt: Date.now(), })) } this.persist() }
getDraft(key: string) { return this.drafts.find(d => d.key === key) ?? null }
@modelAction discardDraft(key: string) { this.drafts = this.drafts.filter(d => d.key !== key) this.persist() }
@computed get pendingDrafts() { return this.drafts.filter(d => d.isNew) }
@computed get unsavedEdits() { return this.drafts.filter(d => !d.isNew) }
persist = debounce(() => { db.execute( 'INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)', ['drafts', JSON.stringify(getSnapshot(this))] ) }, 500)
static async load(): Promise<DraftStore> { const row = await db.execute('SELECT value FROM kv WHERE key = ?', ['drafts']) const snapshot = row.rows[0]?.value return snapshot ? fromSnapshot<DraftStore>(JSON.parse(snapshot)) : new DraftStore({}) }} Domain model with applyDraft write surface
@model('Todo')class Todo extends Model({ id: prop<string>(), title: prop<string>(), completedAt: prop<number | null>(null), updatedAt: prop<number>(),}) { @computed get isCompleted() { return this.completedAt !== null }
// Single write surface — called by domain functions @modelAction applyDraft(draft: TodoDraft) { this.title = draft.title this.completedAt = draft.completedAt this.updatedAt = Date.now() }
static fromDraft(draft: TodoDraft): Todo { return new Todo({ id: generateId(), title: draft.title, completedAt: draft.completedAt, updatedAt: Date.now(), }) }} Editor component binding to draft
const EditTodoScreen = observer(({ todoId }) => { const todo = store.getTodo(todoId) const draftKey = `todo:${todoId}`
// Restore or create draft const [draft] = useState(() => draftStore.getDraft(draftKey) ?? draftStore.createDraft(draftKey, TodoDraft.fromTodo(todo)) )
return ( <> <TextInput value={draft.title} onChangeText={action(v => draft.title = v)} /> <Button disabled={!draft.isValid} onPress={() => { commitTodoEdit(todo, draft) draftStore.discardDraft(draftKey) }} /> </> )}) Patch-to-SQL translator function
import { type Patch } from 'mobx-keystone'import { eq } from 'drizzle-orm'
const isEphemeral = (field: string) => field.startsWith('_') || field.startsWith('$')
function patchToSQL(patch: Patch): (() => Promise<void>) | null { const { op, path, value } = patch if (path.length < 2) return null
const [mapName, id, ...fieldPath] = path
switch (mapName) { case 'todosMap': return buildEntityPatch(todos, op, String(id), fieldPath, value) case 'listsMap': return buildEntityPatch(lists, op, String(id), fieldPath, value) default: return null }}
function buildEntityPatch( table: SQLiteTable, op: string, id: string, fieldPath: (string | number)[], value: unknown,) { // add at map level = INSERT if (op === 'add' && fieldPath.length === 0) { return () => db.insert(table).values(value as any) .onConflictDoNothing().then(() => {}) }
// remove at map level = DELETE if (op === 'remove' && fieldPath.length === 0) { return () => db.delete(table) .where(eq(table.id, id)).then(() => {}) }
// replace at field level = UPDATE if (op === 'replace' && fieldPath.length === 1) { const field = String(fieldPath[0]) if (isEphemeral(field)) return null const column = table[field as keyof typeof table] if (!column) return null
return () => db.update(table) .set({ [field]: value } as any) .where(eq(table.id, id)).then(() => {}) }
return null} Action tracking middleware for grouped SQL persistence
import { actionTrackingMiddleware, patchRecorder } from 'mobx-keystone'
function attachPersistence(store: RootStore) { let recorder = patchRecorder(store, { recording: false })
actionTrackingMiddleware(store, { filter: (ctx) => ctx.parentContext === undefined, // top-level only
onStart: () => { recorder.recording = true },
onFinish: async (ctx, ret) => { recorder.recording = false
if (ret.result === 'return') { // Action succeeded — persist all patches as one SQL transaction const patches = recorder.events.flatMap(e => e.patches) await flushGrouped(patches) } // Action threw — patches already reverted, nothing to persist
recorder.events.splice(0) }, })} Merged patch flush with grouped UPDATE statements
async function flushGrouped(patches: Patch[]) { const updates = new Map<string, Record<string, unknown>>() const inserts: unknown[] = [] const deletes: string[] = []
for (const { op, path, value } of patches) { const [mapName, id, field] = path if (isEphemeral(String(field))) continue
if (op === 'add' && !field) { inserts.push(value) } else if (op === 'remove' && !field) { deletes.push(String(id)) } else if (op === 'replace' && field) { const key = `${mapName}:${id}` updates.set(key, { ...updates.get(key), [String(field)]: value }) } }
return db.transaction(async (tx) => { for (const [key, fields] of updates) { const [, id] = key.split(':') await tx.update(todos).set(fields).where(eq(todos.id, id)) } for (const data of inserts) { await tx.insert(todos).values(data as any) } for (const id of deletes) { await tx.delete(todos).where(eq(todos.id, id)) } })}
// Result: completeTodo() that sets completedAt + updatedAt produces:// UPDATE todos SET completed_at = ?, updated_at = ? WHERE id = ?// — one statement, not two Undo/redo with inverse patches and automatic SQL rollback
import { onPatches, applyPatches, type Patch } from 'mobx-keystone'
type HistoryEntry = { patches: Patch[] inversePatches: Patch[]}
const history: HistoryEntry[] = []const redoStack: HistoryEntry[] = []let isUndoRedoing = false
onPatches(store, (patches, inversePatches) => { if (isUndoRedoing) return // don't record undo ops as history history.push({ patches, inversePatches }) redoStack.length = 0 // clear redo stack on new action})
function undo() { const last = history.pop() if (!last) return redoStack.push(last) isUndoRedoing = true applyPatches(store, last.inversePatches, true) isUndoRedoing = false // persistence middleware fires automatically with compensating SQL}
function redo() { const next = redoStack.pop() if (!next) return history.push(next) isUndoRedoing = true applyPatches(store, next.patches) isUndoRedoing = false} Using Keystone's built-in undoMiddleware
import { undoMiddleware } from 'mobx-keystone'
const undoManager = undoMiddleware(store)
// Each domain function call = one undo step// Regardless of how many fields changed inside applyDraft
undoManager.canUndo // true after first commitundoManager.undo() // reverts entire draft commit atomicallyundoManager.redo() // reapplies it Service with injected AppDatabase
// Production — op-sqlite on deviceimport { drizzle } from 'drizzle-orm/op-sqlite'import { open } from '@op-engineering/op-sqlite'export const db = drizzle(open({ name: 'app.db' }), { schema })
// Tests — better-sqlite3 in Nodeimport { drizzle } from 'drizzle-orm/better-sqlite3'import Database from 'better-sqlite3'export function createTestDb() { const db = drizzle(new Database(':memory:'), { schema }) return db}
// Service works with both — unchangedclass TodoService { constructor(private db: AppDatabase) {}
async complete(id: string) { const todo = await this.db.select().from(todos) .where(eq(todos.id, id)).then(rows => rows[0]) if (todo.blockedBy) throw new Error('Cannot complete a blocked todo') await this.db.update(todos) .set({ completedAt: Date.now() }) .where(eq(todos.id, id)) }} In-memory test database factory with pushSchema
import { drizzle } from 'drizzle-orm/better-sqlite3'import Database from 'better-sqlite3'import { pushSchema } from 'drizzle-kit/api'import * as schema from '../db/schema'
async function createTestDb() { const sqlite = new Database(':memory:') const db = drizzle(sqlite, { schema })
// Push current schema — no migration files needed const { apply } = await pushSchema(schema, db) await apply()
return db} Per-test database isolation
describe('TodoService', () => { let db: AppDatabase let service: TodoService
beforeEach(async () => { db = await createTestDb() service = new TodoService(db) })
it('cannot complete a blocked todo', async () => { await db.insert(todos).values({ id: '1', title: 'Test', listId: 'list-1', blockedBy: '2', createdAt: Date.now(), })
await expect(service.complete('1')).rejects.toThrow('blocked') })
it('sets completedAt on completion', async () => { await db.insert(todos).values({ id: '1', title: 'Test', listId: 'list-1', createdAt: Date.now(), })
await service.complete('1')
const result = await db.select().from(todos) .where(eq(todos.id, '1')) expect(result[0].completedAt).toBeDefined() })}) Device test for reactive query firing
// Runs on device/simulator onlydescribe('Reactive queries', () => { it('fires callback when watched table changes', async () => { const db = open({ name: ':memory:' }) await db.execute('CREATE TABLE items (id TEXT PRIMARY KEY, name TEXT)')
const results: unknown[][] = []
db.reactiveExecute({ query: 'SELECT * FROM items', arguments: [], tables: ['items'], callback: (rows) => results.push(rows), })
await db.execute("INSERT INTO items VALUES ('1', 'test')")
// Allow reactive callback to fire await new Promise(r => setTimeout(r, 50))
expect(results.length).toBeGreaterThan(0) expect(results[results.length - 1]).toEqual([{ id: '1', name: 'test' }]) })})