Web

Experimental support for moor and webapps.

Starting from moor 1.6, you can experimentally use moor in Dart webapps. Moor web supports Flutter Web, AngularDart, plain dart:html or any other web framework.

Getting started

Instead of depending on moor_flutter in your pubspec, you need to depend on on moor directly. Apart from that, you can follow the getting started guide. Also, instead of using a FlutterQueryExecutor in your database classes, you can use a WebDatabase executor:

import 'package:moor/moor_web.dart';

@UseMoor(tables: [Todos, Categories])
class MyDatabase extends _$MyDatabase {
  // here, "app" is the name of the database - you can choose any name you want
  MyDatabase() : super(WebDatabase('app'));

Moor web is built on top of the sql.js library, which you need to include:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script defer src="sql-wasm.js"></script>
    <script defer src="main.dart.js" type="application/javascript"></script>
</head>
<body></body>
</html>

You can grab the latest version of sql-wasm.js and sql-wasm.wasm here and copy them into your web folder.

A full example that works on the web (and all other platforms) is available here.

Gotchas

The database implementation uses WebAssembly, which needs to be supported by your browser. Also, make sure that your webserver serves the .wasm file as application/wasm, browsers won't accept it otherwise.

Sharing code between native apps and web

If you want to share your database code between native applications and webapps, just import the basic moor library and make the QueryExecutor configurable:

// don't import moor_web.dart or moor_flutter/moor_flutter.dart in shared code
import 'package:moor/moor.dart';

@UseMoor(/* ... */)
class SharedDatabase extends _$MyDatabase {
    SharedDatabase(QueryExecutor e): super(e);
}

With native Flutter, you can create an instance of your database with

import 'package:moor_flutter/moor_flutter.dart';
SharedDatabase constructDb() {
    return SharedDatabase(FlutterQueryExecutor.inDatabaseFolder(path: 'db.sqlite'));
}

On the web, you can use

import 'package:moor/moor_web.dart';
SharedDatabase constructDb() {
    return SharedDatabase(WebDatabase('db'));
}

Debugging

You can see all queries sent from moor to the underlying database engine by enabling the logStatements parameter on the WebDatabase - they will appear in the console. When you have assertions enabled (e.g. in debug mode), moor will expose the underlying database object via window.db. If you need to quickly run a query to check the state of the database, you can use db.exec(sql). If you need to delete your databases, there stored using local storage. You can clear all your data with localStorage.clear().

Web support is experimental at the moment, so please report all issues you find.

Using IndexedDb

The default WebDatabase uses local storage to store the raw sqlite database file. On browsers that support it, you can also use IndexedDb to store that blob. In general, browsers allow a larger size for IndexedDb. The implementation is also more performant, since we don't have to encode binary blobs as strings.

To use this implementation on browsers that support it, replace WebDatabase(name) with:

WebDatabase.withStorage(await MoorWebStorage.indexedDbIfSupported(name))

Moor will automatically migrate data from local storage to IndexedDb when it is available.

Using web workers

Starting from moor 4.1, you can offload the database to a background thread by using Web Workers. Moor also supports shared workers, which allows you to seamlessly synchronize query-streams and updates across multiple tabs!

Since web workers can't use local storage, you need to use MoorWebStorage.indexedDb instead of the regular implementation.

To write a web worker that will serve requests for moor, create a file called worker.dart in the web/ folder of your app. It could have the following content:

import 'dart:html';

import 'package:moor/moor.dart';
import 'package:moor/moor_web.dart';
import 'package:moor/remote.dart';

void main() {
  final self = SharedWorkerGlobalScope.instance;
  self.importScripts('sql-wasm.js');

  final db = WebDatabase.withStorage(MoorWebStorage.indexedDb('worker',
      migrateFromLocalStorage: false, inWebWorker: true));
  final server = MoorServer(DatabaseConnection.fromExecutor(db));

  self.onConnect.listen((event) {
    final msg = event as MessageEvent;
    server.serve(msg.ports.first.channel());
  });
}

For more information on this api, see the remote API.

Connecting to that worker is very simple with moor's web and remote apis. In your regular app code (outside of the worker), you can connect like this:

import 'dart:html';

import 'package:moor/remote.dart';
import 'package:moor/moor_web.dart';
import 'package:web_worker_example/database.dart';

DatabaseConnection connectToWorker() {
    final worker = SharedWorker('worker.dart.js');
    return remote(worker.port!.channel());
}

You can pass that DatabaseConnection to your database by enabling the generate_connect_constructor build option. For more information on the DatabaseConnection class, see the documentation on isolates.

A small, but working example is available under extras/web_worker_example in the moor repository.