Getting Started
Install kunii and use it with D1 or Durable Object SQLite
Install
npm install @kuratchi/kunii
D1 usage
Bind the ORM directly to a Cloudflare D1 database:
import { kunii } from '@kuratchi/kunii';
import { env } from 'cloudflare:workers';
import { appSchema } from './server/schemas/app';
const db = kunii(env.DB, appSchema);
Pass the schema and you get JSON path queries, soft-delete filtering, and insert validation. Without a schema the ORM still works for raw queries — but JSON columns stay opaque.
For module-level singletons, prefer lazy binding so the database is resolved at query time:
const db = kunii(() => env.DB, appSchema);
Basic D1 operations
await db.todos.insert({ title: 'Hello' });
const todos = await db.todos
.orderBy({ created_at: 'desc' })
.many();
Auto-migration on cold start
For Cloudflare-hosted framework apps, use the adapter that matches where your
framework exposes Cloudflare env. The adapter runs D1 migrations on the first
request per worker isolate and then gives you schema-aware clients.
Plain Workers/Wrangler:
import { initKuniiORM } from '@kuratchi/kunii';
import { appSchema } from './server/schemas/app';
const orm = initKuniiORM({ DB: appSchema });
export default {
async fetch(request, env, ctx) {
await orm.migrateOnce(env);
const db = orm.db(env, 'DB');
const todos = await db.todos.many();
return Response.json(todos.data);
},
};
Koze:
// src/middleware.ts
import { defineMiddleware } from '@kuratchi/koze';
import { initKuniiORM } from '@kuratchi/kunii/koze';
import { appSchema } from './server/schemas/app';
const orm = initKuniiORM({ DB: appSchema });
export default defineMiddleware({
orm: orm.middleware(),
});
The older autoMigrate({ DB: appSchema }) middleware helper is still
supported. See Framework Adapters for SvelteKit,
Next, Nuxt, Astro, Workers, and Koze examples.
Durable Object SQLite usage
Inside a Durable Object, initialize synchronously in the constructor:
import { DurableObject } from 'cloudflare:workers';
import { initKuniiDO } from '@kuratchi/kunii';
import { appSchema } from './schemas/app';
export class AppDO extends DurableObject {
db;
constructor(ctx, env) {
super(ctx, env);
this.db = initKuniiDO(ctx.storage, appSchema);
}
async list() {
return this.db.todos.many();
}
}
initKuniiDO(ctx.storage, schema) runs CREATE TABLE IF NOT EXISTS plus additive ALTER TABLE ADD COLUMN for any new columns, then returns the query client. It's idempotent across DO restarts.
Pass ctx.storage (preferred) so atomic db.batch() can use transactionSync. Pass ctx.storage.sql if you only need the SQL surface.
Helpful errors
If a query hits a missing table, the ORM rewrites the error to point you at autoMigrate:
no such table: users. Did you forget to wire `autoMigrate({ <BINDING>: schema })`
into your middleware? See [Migrations](/docs/kunii/migrations) for details.
For an opt-in dev-mode sanity check, drop assertSchemaInSync into your middleware and it'll throw if the live database has drifted:
import { assertSchemaInSync } from '@kuratchi/kunii';
export default defineMiddleware({
schemaCheck: import.meta.env.DEV
? assertSchemaInSync({ DB: appSchema })
: null,
// ...
});
Core exports
-
initKuniiORM({ BINDING: schema })from framework adapter paths - D1 migration/client wiring for Cloudflare-hosted apps -
initKuniiDO(storage, schema)fromkunii- synchronous DO setup plus query client -
kunii(binding, schema?)— query client -
autoMigrate(storage, schema)— synchronous DO DDL primitive -
autoMigrate({ BINDING: schema })— D1 middleware step -
assertSchemaInSync({ BINDING: schema })— dev-mode drift check -
createRuntimeOrm,createSchemaClient— lower-level building blocks -
Schema helpers from
kunii -
Migration helpers from
kunii/migrations(runMigrations,buildInitialSql,buildDiffSql,diffSchemas) for BYO migration workflows