# Sqflite Common Ffi Web Options

> Use when customizing or debugging sqflite on the web with package:sqflite_common_ffi_web beyond the default factory: createDatabaseFactoryFfiWeb, SqfliteFfiWebOptions (sqlite3WasmUri, sharedWorkerUri, indexedDbName, inMemory, forceAsBasicWorker), serving sqlite3.wasm and sqflite_sw.js from another location or name, the sw_js_file pubspec override, several isolated IndexedDB stores, sqfliteFfiWebLoadSqlite3Wasm and sqfliteFfiWebStartSharedWorker, SqfliteFfiWebContext, sqliteFfiWebDebugWebWorker, and inspecting the worker in Chrome (chrome://inspect/#workers).

- Skill: `tekartik/sqflite-common-ffi-web-options` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tekartik/sqflite-common-ffi-web-options`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tekartik/sqflite-common-ffi-web-options/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tekartik (https://skillmd.com/u/tekartik)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tekartik/sqflite-common-ffi-web-options

---


# sqflite_common_ffi_web: custom factories, options and debugging

The default `databaseFactoryFfiWeb` loads `sqflite_sw.js` and `sqlite3.wasm`
relative to the page and stores everything in the IndexedDB database
`sqflite_databases`. `createDatabaseFactoryFfiWeb` builds a factory with other
locations, another store name or no worker. Basic setup is covered by
`sqflite-common-ffi-web-setup`.

```dart
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';

final databaseFactoryCustomWeb = createDatabaseFactoryFfiWeb(
  options: SqfliteFfiWebOptions(
    sharedWorkerUri: Uri.parse('/assets/sqflite_sw.js'),
    sqlite3WasmUri: Uri.parse('/assets/sqlite3.wasm'),
    indexedDbName: 'my_app_databases',
  ),
);
```

## Guidelines

### createDatabaseFactoryFfiWeb

* Signature: `createDatabaseFactoryFfiWeb({SqfliteFfiWebOptions? options,
  bool noWebWorker = false, String? tag})`. Web only; on io it throws
  `UnsupportedError`.
* Create the factory once (top-level `final` or a singleton). Each factory
  starts its own worker connection / wasm instance on first use, guarded by a
  lock, and keeps it for the life of the page.
* `noWebWorker: true` loads the wasm in the main thread using the same
  options (`sqlite3WasmUri`, `indexedDbName`); `sharedWorkerUri` is ignored.
  `databaseFactoryFfiWebNoWebWorker` is this with default options.
* `tag` (default `ffi_web`) only labels the factory in logs and
  `toString()`.
* Options are sent to the worker before the first database call
  (`setWebOptions`) and the worker loads the wasm and opens the IndexedDB
  store once, at its first database call. A shared worker is keyed by its
  script URL and serves every tab and every factory using that URL, so
  options arriving later (another tab, a second factory with the same
  `sharedWorkerUri`) do not change a running worker. Use one option set per
  worker script; for a different `indexedDbName` or `sqlite3WasmUri` use a
  different `sharedWorkerUri` (a copy of the worker file) or
  `noWebWorker: true`.

### SqfliteFfiWebOptions

* `sqlite3WasmUri` (default `sqlite3.wasm` relative to the page in
  no-worker mode, and relative to the worker script otherwise). Use an
  absolute path (`/sqlite3.wasm`) when the app is served from nested routes.
* `sharedWorkerUri` (default `sqflite_sw.js`): the worker script produced by
  setup. Rename or version it (`sqflite_sw_v2.js`) to force browsers to
  reload the worker after a package upgrade; the setup output name can be
  fixed in the app `pubspec.yaml`:

  ```yaml
  sqflite:
    sqflite_common_ffi_web:
      sw_js_file: sqflite_sw_v2.js
  ```

  then re-run `dart run sqflite_common_ffi_web:setup` and pass the same name
  in `sharedWorkerUri`.
* `indexedDbName` (default `sqflite_databases`): the IndexedDB database
  hosting the virtual file system. Factories with different names (and
  different workers, see above) are fully isolated stores, for example
  production data vs. a scratch store.
* `inMemory`: declared and transported, but the current loader always opens
  the IndexedDB file system; do not rely on it. Use `inMemoryDatabasePath`
  as the database path for a non-persistent database instead.
* `forceAsBasicWorker` is `@visibleForTesting` (used by
  `databaseFactoryFfiWebBasicWebWorker`): forces a dedicated `Worker`
  instead of a `SharedWorker`. Do not set it in application code.
* `SqfliteFfiWebOptionsExt.toMap()` serializes options (useful for logging).

### Lower-level entry points

* `sqfliteFfiWebLoadSqlite3Wasm(options)` opens the IndexedDB file system,
  fetches the wasm and returns a `SqfliteFfiWebContext` (main thread, what
  `noWebWorker` does). `sqfliteFfiWebStartSharedWorker(options)` spawns the
  shared (or basic) worker and returns a context that forwards messages.
  Both are only needed to build a custom worker or to preload; normal code
  uses the factories.
* `SqfliteFfiWebContext` exposes `options`; the web-only extension adds `fs`
  (`VirtualFileSystem` from `package:sqlite3/wasm.dart`), `wasmSqlite3`,
  `sharedWorker` and `sendRawMessage(message)`. The extension lives in
  `src/` and is not part of the public export; treat it as unstable.

### Debugging

* Set `sqliteFfiWebDebugWebWorker = true` (setter is `@Deprecated('testing
  only')`; ignore the warning in a debug build) before the first call to log
  every message sent to and received from the worker (`main_send:` /
  `main_recv:`) and worker start-up steps.
* Inspect the shared worker (console, breakpoints, network) in Chrome at
  `chrome://inspect/#workers`. Application > IndexedDB shows the
  `sqflite_databases` store.
* The database is tied to the origin including the port: keep the same dev
  server port between runs.
* A failure at the first call with the console error "An error occurred
  while initializing the web worker" means the worker script could not be
  loaded (wrong `sharedWorkerUri`, setup not run, file not deployed); the
  message names the URL tried.
* Wrong or outdated `sqlite3.wasm` shows as a load failure inside the worker
  (visible in the worker console); re-run setup with `--force` or download
  the wasm matching the resolved `sqlite3` version.

## Examples

### App served under a sub path with versioned worker

```dart
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';

// Files deployed as /app/sqflite_sw_v2.js and /app/sqlite3.wasm
final DatabaseFactory appDatabaseFactory = createDatabaseFactoryFfiWeb(
  options: SqfliteFfiWebOptions(
    sharedWorkerUri: Uri.parse('/app/sqflite_sw_v2.js'),
    sqlite3WasmUri: Uri.parse('/app/sqlite3.wasm'),
  ),
);

Future<Database> openAppDatabase() =>
    appDatabaseFactory.openDatabase('app.db');
```

### Separate IndexedDB store for tests or scratch data

```dart
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';

// web/sqflite_sw_scratch.js is a copy of web/sqflite_sw.js: a distinct
// worker URL gives a distinct worker, so it can use its own store.
final DatabaseFactory scratchFactory = createDatabaseFactoryFfiWeb(
  options: SqfliteFfiWebOptions(
    sharedWorkerUri: Uri.parse('sqflite_sw_scratch.js'),
    indexedDbName: 'scratch_databases',
  ),
  tag: 'ffi_web_scratch',
);

Future<void> resetScratch() async {
  // Same name as production but a different store: no collision.
  await scratchFactory.deleteDatabase('app.db');
}
```

### Main thread factory with a custom wasm location

```dart
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';

final toolFactory = createDatabaseFactoryFfiWeb(
  noWebWorker: true,
  options: SqfliteFfiWebOptions(sqlite3WasmUri: Uri.parse('/wasm/sqlite3.wasm')),
);
```

### Enabling worker message logging in debug builds

```dart
import 'package:flutter/foundation.dart' show kDebugMode, kIsWeb;
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';

void enableSqfliteWebLogs() {
  if (kIsWeb && kDebugMode) {
    // ignore: deprecated_member_use
    sqliteFfiWebDebugWebWorker = true;
  }
}
```

## Common mistakes

* Creating a new factory per call: each one spins up its own worker
  connection; keep one instance.
* Changing `indexedDbName` after users have data: the old store is
  orphaned, data appears lost.
* Two factories with different `indexedDbName` but the same worker script:
  both talk to the same running worker and the same store.
* Setting `sharedWorkerUri` without deploying a file at that URL, or
  changing the setup output name without updating the option (or the
  reverse).
* Relying on `inMemory: true` for a private database.
* Using `forceAsBasicWorker` in production code.
* Reading `SqfliteFfiWebContext.fs` from application code (unstable, `src/`).

