Skip to main content
Version: 0.27.1

Database Adapters

In this guide, you'll learn how to add support for new databases and new platforms to WatermelonDB.

Introduction

WatermelonDB is designed to be database-agnostic. It's a frontend JavaScript database framework, but its high-level abstractions can be plugged in to any underlying database, platform, or UI framework. We call the translation layer between underlying databases and high-level WatermelonDB APIs database adapters.

Currently supported databases

SQLite

Supported frameworks:

  • React Native:
    • Operating systems:
      • iOS
      • Android
    • Implementations:
      • JSI adapter
      • New NativeModule (added in 0.26)
      • Legacy NativeModule (deprecated in 0.26)
  • NodeJS
    • via better-sqlite3 - contributed by Sid Ferreira

LokiJS

Supported frameworks:

  • Web
    • Storage: IndexedDB
  • NodeJS
    • Storage: in-memory only

Why LokiJS? WebSQL would be a perfect fit for Watermelon, but sadly is a dead API, so we must use IndexedDB, but its querying capabilities make it unsuitable as a serious database. LokiJS implements a very fast in-memory querying API, using IndexedDB as storage.

Contribute these adapters!

Please contribute to WatermelonDB. We'd love to support these platforms and databases:

Adding new React Native operating systems

Thanks to our cross-platform JSI (C++) SQLite adapter, it takes very little code to add support for new React Native platforms (like macOS or Windows).

All you have to do is this:

  • Compile .cpp files in native/shared folder
  • Link library with sqlite3
    • Use system-provided sqlite3 if possible (we do that on iOS)
    • If not, we ship sqlite source code via NPM @nozbe/sqlite package. Just add node_modules/@nozbe/sqlite/** to search paths and compile node_modules/@nozbe/sqlite/*/sqlite3.c
  • Provide implementation for native/shared/DatabasePlatform.h
    • Please note that most of these functions can remain unimplemented (empty) for basic operation - e.g. you can skip logging, memory, turbo json support
  • Provide a React Native hook that calls Database::install(jsi::Runtime *)

Check out native/android-jsi and native/ios for two implementation examples. You might be able to reuse some code from these, e.g. platform support stubs or CMakeLists.txt.

Adding new frameworks to SQLite adapter

Let's say you want to add support for a new JS+native framework, like Electron, Tauri, NativeScript or Capacitor.

This takes more work, but ultimately, given that (iOS, Android, JS, C++, Objective-C, Java) are supported already (just for React Native and Node), you only need to develop the glue code necessary to bridge the gap between src/adapters/sqlite JS code, and the native but non-React-Native-specific bits. You'll need some familiarity with the platform you're trying to support, but little WatermelonDB/React Native/C++ familiary will be needed to get this done.

JS-side glue

The general SQLite implementation is in src/adapters/sqlite/index.js. It forwards database calls to this._dispatcher. The dispatcher is the JS-side bridge/glue code.

See src/adapters/sqlite/makeDispatcher to see concrete dispatchers and add your own, depending on the platform's convention of calling native code. For example:

  • makeDispatcher/index.js (Node JS) just imports more JS code, since native=JS in this case
  • makeDispatcher/index.native.js (React Native) calls require('react-native').NativeModules

Native-side glue

Depending on the capabilities of the framework you want to support, there's a few ways to go about this:

The easy (JS-only) way. If your framework has existing SQLite bindings in JavaScript that work synchronously (similar to better-sqlite3 in Node), you can reuse code in src/adapters/sqlite/sqlite-node

The Java/Objective-C way. If your framework targets iOS, macOS, or Android, and you're terrified of C++, you can reuse the React Native NativeModule implementation.

  • Look at native/ios/WatermelonDB/objc/WMDatabase.{h,m} and WMDatabaseDriver.{h,m} for the iOS implementation. These files contain SQLite, WatermelonDB, and iOS-specific logic, but without React Native details. You need to provide an equivalent of WMDatabaseBridge (the React Native glue between WMDatabaseDriver and JS) for your framework
  • For Android, look at native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java and WMDatabaseDriver.java

The C++ way. The best way is to refactor the React Native C++ JSI module to split off React Native-specific logic and leave a framework-independent core. Doing this way ensures that your port will receive support for new operating systems, and all the new features, as the core React Native module will focus on the C++ implementation in the long term. Contact @radex for guidance about this.

Adding new databases

If you want to contribute support to new underlying databases (i.e. not SQL or LokiJS-based), this is a rough sketch of what's required:

  • A new FoodbAdapter that conforms to DatabaseAdapter (src/adapters/type.js). You can initially skip some method implementations for basic support, most basic are find, query, count, batch.
  • Some way to convert WatermelonDB's query language into queries specific for your database. For reference, see:
    • src/adapters/sqlite/encodeQuery for generating SQL
    • src/adapters/lokijs/worker/encodeQuery for generating LokiJS queries + executeQuery which executes joins (which Loki does not natively support)