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)
- Operating systems:
- NodeJS
- via
better-sqlite3
- contributed by Sid Ferreira
- via
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:
- React Native for Windows and macOS
- Realm database
- SQLite for web (sql.js or absurd-sql)
- LokiJS NodeJS storage option
- SQLite for Electron, Tauri, etc.
- SQLite for Capacitor
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 innative/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 addnode_modules/@nozbe/sqlite/**
to search paths and compilenode_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 casemakeDispatcher/index.native.js
(React Native) callsrequire('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}
andWMDatabaseDriver.{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 ofWMDatabaseBridge
(the React Native glue betweenWMDatabaseDriver
and JS) for your framework - For Android, look at
native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java
andWMDatabaseDriver.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 toDatabaseAdapter
(src/adapters/type.js
). You can initially skip some method implementations for basic support, most basic arefind, 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 SQLsrc/adapters/lokijs/worker/encodeQuery
for generating LokiJS queries +executeQuery
which executes joins (which Loki does not natively support)