Prisma IDB FaviconPrisma IDB

Client

Open an auto-migrating IndexedDB client for browser code

createAutoMigratingIdbClient takes a bundled contractSpace and opens the database, applying any pending migrations before resolving.

import { createAutoMigratingIdbClient } from "@prisma-next-idb/client-idb/client-auto";
import type { IdbClient } from "@prisma-next-idb/client-idb/client-auto";
import type { Contract } from "./prisma/contract";
import { contractSpace } from "./prisma/contract-space.generated";

type Db = IdbClient<Contract>;

let db: Promise<Db> | null = null;

export function getDb(): Promise<Db> {
  return (db ??= createAutoMigratingIdbClient({ contractSpace, dbName: "my-app" }));
}

The promise is cached so the database only opens once. Call getDb() from any browser-side code.

Destructive migrations

By default, the client refuses to apply migrations that drop object stores or indexes. Opt in explicitly after reviewing the migration:

createAutoMigratingIdbClient({
  contractSpace,
  dbName: "my-app",
  policy: { onDestructive: "allow" },
});

Local IndexedDB may contain data that exists only on the user's device — drafts, cached state, offline work. Only allow destructive operations when you are certain the data can be discarded.

Client shape

Property / methodDescription
db.ormTyped model accessors
db.verifyMarker()Check that the open database matches the contract
db.withTransaction(…)Low-level transaction scope for advanced integrations
db.close()Close the underlying IndexedDB connection

Extensions

An IDB extension contributes its own object stores and its own independently-versioned migration history, applied alongside your app schema in the same upgradeneeded transaction. Pass one or more via extensions:

import { createAutoMigratingIdbClient } from "@prisma-next-idb/client-idb/client-auto";
import { idbSyncExtension } from "@prisma-next-idb/sync-extension-idb/control";

const db = await createAutoMigratingIdbClient({
  contractSpace,
  dbName: "my-app",
  extensions: [idbSyncExtension],
});

@prisma-next-idb/sync-extension-idb is the first real extension — see Sync. You never author or generate an extension's migrations yourself; the package ships them and versions them independently of your app's own migration history.

Managed client

createManagedAutoIdbClient combines createAutoMigratingIdbClient with a module-level singleton and a race-safe reset() for wiping local data, e.g. on logout — the auto-migrate options (contractSpace, dbName, …) go in once:

import { createManagedAutoIdbClient } from "@prisma-next-idb/client-idb/client-auto";

const managedDb = createManagedAutoIdbClient({ contractSpace, dbName: "my-app" });

export const getDb = () => managedDb.get();
export const closeDb = () => managedDb.close();
export const resetDb = () => managedDb.reset();
MethodDescription
get()Opens on first call and caches the result; concurrent calls share one open
close()Closes the cached client, if any
reset()Closes and deletes the database, waiting out any in-flight get() first

reset() never races a concurrent get() — a delete is guaranteed not to run while a connection is still opening, and a get() called during a reset waits for the wipe to finish before opening a fresh connection. The kanban example uses this for sign-out, so a different account signing in on the same browser never sees the previous session's local data.

Using sync? @prisma-next-idb/sync-extension-idb/client exports the same wrapper pre-wired to createAutoMigratingSyncIdbClient instead, as createManagedAutoSyncIdbClient — see Sync.

Wrapping a different factory

createManagedAutoIdbClient is createManagedIdbClient pre-wired to createAutoMigratingIdbClient, so dbName only has to be written once. If you need to wrap something else — createIdbClient directly, or your own composition — reach for createManagedIdbClient itself:

import { createManagedIdbClient } from "@prisma-next-idb/client-idb/client";
import { createIdbClient } from "@prisma-next-idb/client-idb/client";

const managedDb = createManagedIdbClient(() => createIdbClient({ contract, dbName: "my-app" }), {
  dbName: "my-app", // must match the factory's own dbName — see below
});

createManagedIdbClient's dbName is used for exactly one thing — reset()'s deleteDatabase call. It's never passed into open() for you, and nothing checks the two agree: if they drift apart (a typo, or the same string duplicated across a refactor), get() still opens the right database, but reset() silently deletes the wrong one — or nothing — while the real data stays put. createManagedAutoIdbClient exists specifically to make that mistake impossible for the common case; prefer it unless you have a genuine reason to compose things yourself.

On this page