Skip to main content
Version: 0.30.0-beta.8

Changelog

All notable changes to this project will be documented in this file.

Contributors: Please add your changes to CHANGELOG-Unreleased.md

0.30.0-beta.8 - 2026-08-27

BREAKING CHANGES

  • Android Java/JNI package renamed from com.nozbe.watermelondb to com.nitromelondb. Update any R8/Proguard keep rule (-keep class com.nitromelondb.** { *; }) and any direct NitromelonNative.provideSyncJson imports. The test harness package com.nozbe.watermelonTest is now com.nitromelondb.test.
  • iOS native sources moved from native/ios/WatermelonDB/ to native/ios/NitromelonDB/. The public umbrella header is now #import <NitromelonDB/NitromelonDB.h> (was #import <NitromelonDB/WatermelonDB.h>). The C turbo-sync entry point remains watermelondbProvideSyncJson.

0.30.0-beta.7 - 2026-08-26

0.30.0-beta.6 - 2026-08-26

New features

  • useRecord/useQuery/useObservable hooks (nitromelondb/hooks or nitromelondb/react) as a hooks-based alternative to withObservables. Built on the existing Rx-free experimentalSubscribe* methods; re-renders on record/query changes without cloning records — see docs.
  • useWriter(model, writer) hook: a stable callback that runs writer inside model.database.write(), with writer's first argument typed as the concrete Model subclass passed in. Tracks isPending/error for you, doesn't require writer to be memoized (the latest one passed in is always the one that runs), and stops touching isPending/error (without cancelling the write itself) if the component unmounts before it settles — see docs.
  • useAtomicWriter(modelClass, record, builder) hook: the narrower sibling of useWriter for the single most common write — pass a record and it updates it, leave it out (or pass null/undefined) and it creates a new one of modelClass instead. No free-form writer function, just the same field-setting builder .create()/.update() already take, which keeps the Writer to nothing but field assignments (no risk of slow/unrelated logic stalling every other write in the app). Resolves the database itself via useDatabase() context, so a <DatabaseProvider> is required — see docs.
  • useObservable now returns [value, { hasEmitted, error }] instead of a bare value, so callers can tell "still on the default" apart from a real emission, and see if the observable errored.
  • Database#resetObservablesCache() / Collection#resetObservablesCache(): forces every actively-subscribed Query observer to drop its cached last emission and refetch. Call this yourself after mutating data outside Watermelon's write path (raw SQL, unsafeExecute, or any manual table wipe that doesn't go through unsafeResetDatabase()) so already-subscribed observers pick up the change instead of going stale — see docs.
  • Database#get() now also accepts a Model class (database.get(Comment)), not just a table name string (database.get('comments')). TableName<T> is just string underneath, so the string form infers nothing and a typo'd table name only fails at runtime; passing the class instead infers Collection<T> for real, since the class is a genuine, checked value. Fully backwards compatible — the string form still works exactly as before.
  • new Database({ seed }), built with databaseSeed() (nitromelondb/Database/seed): seeds initial/demo data via an array of { schemaVersion, run } steps, each tied to the schema version it was written against — the same way a migration's toVersion is — instead of an independent counter you have to remember to bump. A step runs at most once, ever, once the database has actually reached its schemaVersion (immediately for a fresh install on the latest schema, after migrating for an existing install catching up); deliberately kept separate from schema migrations rather than folded into them, since run can freely be async (await fetch(...), read a file, etc.) where a migration step can't. Every write()/read()/batch() call and every direct Collection/Query read (find(), query().fetch(), query().fetchCount(), observe*()/experimentalSubscribe*(), etc.) issued after construction is queued until every pending step resolves — no more hand-rolled "seed in a data hook" pattern with its own idempotency flag and its own race against the first render. In development, an access that has to wait logs a warning once, since it usually means something is touching the database earlier than intended. A step can also declare retries (0 by default) to retry immediately, in-process, that many extra times before being treated as a real failure — for a step whose run is occasionally flaky (e.g. a network fetch) rather than reliably broken. A failing step (retries exhausted, if any) is reported via onError (with which schemaVersion failed) or logger.error if omitted, and doesn't get the database stuck, nor run later steps — that step (and everything after it) is simply retried on the next launch. Database#unsafeResetDatabase() reapplies seed once the reset itself is done and resolves only once that's finished too — the reset already wipes the durable marker tracking which steps ran (same underlying adapter-level local storage the reset's own contract already says it clears), so this just follows through on what that already implies instead of silently leaving seeded data un-reapplied after a reset; pass { reapplySeed: false } to opt out for a particular reset (e.g. a "wipe everything" debug action, as opposed to a logout/login). Progress is logged ([Database] Running seed step for schema version N / ... completed, and a warning per failed retry attempt) the same way schema setup/migrations already are, so seeding isn't a silent step in your startup logs. onDone({ durationMs, stepsRun }) fires once every pending step for a run has succeeded (never alongside onError) — durationMs covers just the pending-steps loop, and stepsRun is which schema versions actually ran (not ones skipped as already-applied), for telemetry beyond just "did it finish."
  • useDatabaseReady(database) (nitromelondb/hooks or nitromelondb/react) is now documented in Hooks alongside the other hooks (it shipped in an earlier Unreleased entry above but was missing from the hooks guide).
  • Database#readyPromise / Database#isReady: resolves / is true once schema setup/migrations and any pending seed steps have settled. Purely observational — every read/write already queues correctly without them — for gating your own bootstrap UI (e.g. a splash screen) on readiness explicitly, instead of only letting reads/writes queue silently underneath while your UI renders as if the database were already usable.
  • useDatabaseReady(database) hook (nitromelondb/hooks or nitromelondb/react): the readyPromise/isReady pair above as a boolean, with the unmount/stale-database-prop guarding done for you, so a component can gate on it (e.g. if (!useDatabaseReady(database)) return <LoadingScreen />) without hand-rolling a useState/useEffect around readyPromise itself.

Fixes

  • Query#observeCount() (throttled, the default) no longer permanently drops the last write in a burst: throttleTime(250) was leading-only, so a write landing inside an already-open window was silently discarded until a later, unrelated change happened to flush it. Now uses { trailing: true } so the latest count is always eventually emitted. Also fixed SharedSubscribable/Query#observe's underlying _notify() to snapshot its subscriber list before iterating — a subscriber unsubscribing itself from inside its own callback (e.g. a one-shot listener) could otherwise skip notifying the subscriber right after it. Reported upstream as Nozbe/WatermelonDB#1973.
  • Database#unsafeResetDatabase() now calls the above internally for any Query observation cache (SharedSubscribable/KeyedSharedSubscribable) that's still actively subscribed when it runs. Guards against stale/other-user data leaking into a still-mounted component across a logout/login when an app (against the documented contract) leaves a subscription open across a database reset.
  • SQLite Node adapter (Windows): fixed two bugs that made every file-backed test fail there. getPath() only recognized Unix (/…) and file: absolute paths, so a Windows drive-letter path (D:\…) got process.cwd() prepended a second time, producing an unreachable directory. And DatabaseBridge.setUpWithSchema/setUpWithMigrations leaked a file handle: the driver initialize() leaves waiting after a schema/migration error was discarded for a new one without closing it (same root cause as upstream Nozbe/WatermelonDB#1705). Both were invisible on POSIX (unlinking an open file is a no-op there) but permanently blocked deleting/reopening the file on Windows.
  • SQLite Node adapter: added unsafeCloseConnection() — there was no way for a Node/Electron consumer to release a database's native handle at all (only unsafeResetDatabase(), which closes and immediately reopens). Reuses the same shared-memory-aware DatabaseDriver.close() as the leak fix above, so it can't also close a cache=shared in-memory connection another tag still depends on. Node/Electron only; native (iOS/Android/Windows Nitro) has no such lifecycle to expose.

Performance

  • Query#experimentalSubscribeWithColumns/observeWithColumns: multiple subscribers observing the same query with the same columnNames (in any order) now share one underlying subscription (and one re-fetch on change) instead of each running its own, via a new KeyedSharedSubscribable utility — the same shareReplay-style sharing Query#observe/experimentalSubscribe already got from SharedSubscribable.

Changes

  • NotesApp: the 100-note demo seed moved from a useEffect in useNotes.ts (with its own localStorage idempotency flag, racing the first render's count subscription) to database.ts's new seed: { version, run } option, which now owns that idempotency and ordering.
  • NotesApp: rebuilt on top of this release's own hooks. useNotes.ts's hand-rolled useState/useEffect pair (manual subscribe/unsubscribe, a cancelled flag) is now useQuery for the page of notes plus useObservable(query.observeCount()) for the total. Pin/delete moved from a NotesScreen-level deletingIds Set threaded through NotesList/NotesList.windows as props into NoteCard itself, using useWriter for isPending/error per row instead of hand-tracked state (Note#togglePinned/deleteForever dropped @writer since they're now invoked from inside useWriter's own database.write(), and nesting would deadlock).
  • NotesApp Windows renders the Expo NotesApp src/ UI (shared screen/components/model). List on Windows is NotesList.windows.tsx.
  • NotesApp Windows: yarn metro, yarn metro:kill, yarn build:debug / build:release / build:all, yarn start:debug / start:release.

Internal

  • CI: lint GitHub Actions workflows with actionlint and action-validator (yarn lint:workflows) in a separate workflow so a broken ci.yml still fails checks.
  • CI: native/NotesApp jobs wait for ESLint, TypeScript, and JavaScript tests. concurrency cancels superseded PR/master runs (including when a PR is closed).
  • CI: run NotesApp Maestro e2e on Android from a Release APK (embedded JS, no Metro). Build and test stay on the same job for now.
  • NotesApp Maestro: dismiss the IME before tapping Add (API 29 keyboard covers the composer). softwareKeyboardLayoutMode: resize. Consecutive adds wait for the new title; pin/delete use space-free testIDs (Android resource-id) and single taps. Pagination scrolls to Note #100 after inserts.
  • NotesApp Windows e2e: ScrollView of page-sized cards so new rows stay in the UIA tree. addNote types via browser.keys() (same WinAppDriver session as every other command) instead of PowerShell SendKeys — SendKeys required SetForegroundWindow, which CI's foreground-lock silently refuses, so the title field stayed empty and Add was a no-op. textVisible requires isDisplayed(). Windows list keeps off-tree UIA anchors for titles/pin/delete.
  • NotesApp title-input is a normal controlled TextInput (value={title}) on every platform again. It had been made uncontrolled on Windows only (shadow titleRef, defaultValue, a setTimeout-dispatched Add button) to route around PowerShell SendKeys dropping lowercase characters — a WinAppDriver bug, not a reason to change the production component, and it stayed that way even after SendKeys was replaced by browser.keys() above. That made it the actual cause of the remaining flakiness (onChangeText doesn't reliably fire for driver-injected input on an uncontrolled field), which had been getting patched with a growing pile of readback-and-retry-typing logic in addNote instead of fixed at the source. Reverting let addNote/deleteNote shrink to the same click/type/submit/verify shape as pinNote, and made the suite both green and consistently faster.
  • NotesApp Windows e2e: environment.js dumps the full UI Automation tree, a screenshot, and the Root session's top-level window list on any test failure, uploaded as a CI artifact (ci.yml) — webdriver command logs only show what was asked for, not what was actually on screen. This is what found the real cause of the last remaining flakiness: GitHub-hosted Windows runners default to a 1024×768 virtual display (actions/runner-images#2935), smaller than the app's own appWindow.Resize({1000, 1000}) default — so WinAppDriver could still find and "click" elements past the screen edge, but the click landed on pixels that were never painted (pixel-sampling a failure screenshot showed a hard, exact-pixel unpainted edge). Fixed at the source with Set-DisplayResolution -Width 1920 -Height 1080 -Force (a built-in Windows Server ServerCore cmdlet, and GitHub's own confirmed-working max resolution) rather than compensating for a small screen from the app side.
  • NotesApp Windows: schema v3, 100-note seed, sticky Q.skip/Q.take(20) pager, and WinAppDriver UI e2e that mirrors the Maestro flows (replacing the Cavy integration host in CI).
  • CodeQL: build Java/Kotlin from native/androidTest and Swift from WatermelonTester so default-setup autobuild is not required.
  • Simplified AGENTS.md / added Claude Code rules and cwd hooks so agents stop mixing up the library root with examples/NotesApp.
  • NotesApp: disable the Expo dev menu / FAB / onboarding overlay on launch so Maestro e2e can tap the UI.
  • NotesApp: sticky FlashList pager (Q.skip + Q.take(20)), UI moved under src/, Maestro flows for cold start, CRUD, kill-and-relaunch, interaction burst, and pagination.
  • Docs: document NotesApp Maestro e2e and call out device e2e as a fork advantage (README + CONTRIBUTING).
  • NotesApp Maestro: pagination uses Q.take (not Q.limit), Load more no longer inserts rows, subscriptions unsubscribe, sort_order is used for list order.
  • e2e test coverage (Phase 0): Raised iOS XCTest timeout from 100s to 600s, increased Cavy waitTime from 4s to 30s, and created src/adapters/__tests__/sqliteTests/ with helpers.js, index.js, migrations.js, batches.js, concurrency.js, cleanup.js, and databaseLevel.js. Wired the new suite into src/adapters/sqlite/test.js and integrationTest.js so all 5 native consumers (Jest/node, Jest/better-sqlite3, LokiJS, iOS native, Android native, Windows native) run the new file-backed tests.
  • Docs Pages workflow builds with Yarn 4 inside docs-website (root yarn docs:build needs a root install) and only runs when docs-related paths change.
  • Migrate the repo and example apps from Yarn Classic to Yarn 4.18 (node-modules linker, pinned via packageManager / .yarn/releases).
  • Drop patch-package from the library root. There was no root patches/ directory; the Expo expo-modules-jsi workaround stays in the example apps.
  • Pin the Babel 7 toolchain (@babel/core, @babel/cli, plugins, @babel/runtime) to the latest 7.x (7.29.7 / 7.29.8).

0.30.0-beta.5 - 2026-08-17

BREAKING CHANGES

  • Windows: the UWP Paper WMDatabaseBridge JSI installer is removed. { jsi: false } is rejected on Windows the same as iOS/Android.

New features

  • Windows: React Native Windows New Architecture (RNW 0.84 / WinAppSDK) uses Nitro SQLite. Autolinking points at native/windows. Apps spread windowsAppDependencies() from nitromelondb/windows-autolink so react-native-nitro-modules is not searched for a Windows project it does not ship.

Fixes

  • Web: init no longer crashes when window.performance.now exists but is not a function (now.bind is not a function). Timing falls back to Date.now. (#46)
  • Windows: SQLite integration tests run Nitro turbo-sync (unsafeLoadFromSync / provideSyncJson) instead of expecting those APIs to be missing. Memory URI databases open with SQLITE_OPEN_URI and use an in-memory journal so WAL files are not created in a packaged app's cwd. The CI integration app embeds a dev JS bundle (UseDevBundle=true) so the suite runs the same DEV assertions as iOS/Android.

Changes

  • Docs: README and the docs site use the horizontal NitromelonDB logo via a GitHub absolute URL.
  • Docs no longer tell apps to install rxjs by hand. It stays a peer (^7.8.0) for hoisting and a dependency so yarn add nitromelondb installs it.
  • Migration / install docs: remove leftover pod 'simdjson' and clarify that the com.nozbe.watermelondb Proguard keep rule stays.

Internal

  • CI: Windows job uses windows-2025 (VS 2026, SDK 26100, Node 24.19, WinAppDriver). It builds NotesApp and runs the Cavy SQLite integration suite via @react-native-windows/automation.
  • Example apps use Yarn Classic (yarn.lock), not npm package-lock.json.
  • Windows Nitro <NitroModules/…> header map is generated on install / MSBuild (scripts/windows-nitro-shims.mjs), not checked in.

0.30.0-beta.4 - 2026-08-16

Fixes

  • @json sanitizers: input and output types can differ ((source: string) => string[]). The previous Sanitizer<T> = (source: T) => T rejected real-world sanitizers and made wrapping json() fail.

0.30.0-beta.3 - 2026-08-16

Fixes

  • npm: compile .tsx sources into the published tarball. react/withDatabase.js and react/DatabaseProvider.js were missing (import from 'nitromelondb/react' failed in Metro). The JS build only matched .ts/.js, while tsc still emitted their .d.ts.
  • SQLite: log the underlying error when the Nitromelon HybridObject fails to load, instead of only throwing a follow-on "install react-native-nitro-modules" message.

0.30.0-beta.2 - 2026-08-16

Highlights

  • Published package types, exports, rxjs peer, and Nitro peer range so a WatermelonDB → NitromelonDB swap does not need tsconfig path hacks.

New features

  • Documented Observability: nested-writer deadlocks, stuck reader/writer queue warnings, and routing logs into your APM.

Fixes

  • npm types: the published package.json now points types at index.d.ts. Ramda merge was leaving "types": "src/index.ts", which is not in the tarball.
  • @json sanitizers: json<T>() accepts typed sanitizers again ((source: T) => T). memo on the options object is optional.
  • Model.id: assigning record.id = 'custom' inside create() / prepareCreate() works. It throws after create instead of silently no-op'ing.
  • Metro / nitro.json: native require('.../nitro') resolved to package-root nitro.json instead of nitro/index.js, so the HybridObject never loaded in release bundles. Requires now use .../nitro/index.

Changes

  • Docs site version badge tracks the npm package (including alpha/beta). It had stayed on 0.28.0 through the 0.30.0 prereleases.
  • README and docs use the Nitromelon icon, link to full documentation right after the intro, and credit the original WatermelonDB.
  • rxjs is a peer dependency (^7.8.0) as well as a dependency, so Yarn can hoist the host copy.
  • react-native-nitro-modules peer range is >=0.35.2 (was *).
  • Published package includes an exports map for nitromelondb, nitromelondb/decorators, nitromelondb/adapters/sqlite, and other directory imports.
  • Migration guide: do not retarget native/android-jsi or iOS SupportingFiles paths; Jest/Metro mocks; Expo plugin is optional on bare RN; New Architecture required, tested on RN 0.83+.
  • RelationId is exported from the package root.

Internal

  • Publish Release authenticates npm OIDC from a push to master (or manual dispatch). pull_request_target merge tokens are rejected by npm (OIDC token exchange error - package not found).

0.30.0-beta.1 - 2026-08-16

Internal

  • Drop leftover FMDB comments and regenerate native/iosTest CocoaPods so the Xcode project links NitromelonDB (sqlite3 C API), not the old WatermelonDB/FMDB sources.
  • Prepare Release: version bump promote graduates the in-progress alpha/beta to official X.Y.Z (changelog fold). Optional npm dist-tag dropdown defaults to none (channel tags); pick latest only when npm i should install that version.

0.30.0-beta.0 - 2026-08-16

0.30.0-alpha.3 - 2026-08-15

Internal

  • Publish Release omits setup-node registry-url so npm uses OIDC instead of a dummy NODE_AUTH_TOKEN (which caused PUT 404 for a package that already exists).

0.30.0-alpha.2 - 2026-08-15

Fixes

  • Include README.md in the published npm package so the registry page is not empty.

Internal

  • Prepare Release skips versions that already have a git tag, GitHub Release, or npm publish, and only reuses a leftover release/v… branch when none of those exist.
  • Prepare Release folds all same-version alpha/beta changelog entries into one official entry when graduating to a stable release.
  • Publish Release uses setup-node@v6 + Node 24 OIDC as in the npm trusted publishers example (registry-url, package-manager-cache: false). Failed publishes can be retried from Actions → Publish Release.
  • Package author is Stanislav Doskalenko.

0.30.0-alpha.1 - 2026-08-15

Internal

  • Prepare Release accepts version bump none so another alpha/beta of the same X.Y.Z does not require picking patch/minor/major.

0.30.0-alpha.0 - 2026-08-15

BREAKING CHANGES

  • Minimum supported Node.js version is now 22.x (required by React Native 0.87)
  • [iOS] Minimum deployment target is now iOS 15.1
  • [Android] Minimum SDK version is now 24
  • [iOS] CocoaPods spec renamed from WatermelonDB to NitromelonDB. Autolinking picks up the pod. Bridging-header imports, if you have them, are #import <NitromelonDB/WatermelonDB.h>.
  • [iOS] simdjson is vendored in native/vendor/simdjson and compiled into the NitromelonDB pod. Remove any hand-copied pod 'simdjson' Podfile lines; do not add pod 'NitromelonDB' either.
  • [SQLite][RN] iOS/Android SQLite is Nitro-only. NativeModules interop ({ jsi: false }) is removed. The old React Native architecture (Paper / the legacy bridge) is not supported. Windows still uses the JSI installer. Web and Electron keep the Node/better-sqlite3 dispatcher (makeDispatcher/index.ts / index.web.ts).
  • [SQLite][RN] Removed the leftover NativeModule SQLite stack: iOS WMDatabaseBridge / WMDatabaseDriver / FMDB, and the Android Java WMDatabaseBridge / WMDatabase / WMDatabaseDriver APIs. Random IDs use Nitromelon.getRandomIds() on Nitro. Android WatermelonDBPackage still installs the database path and closes SQLite on JS reload.
  • [Android] Removed native/android-jsi (WatermelonDBJSIPackage, libwatermelondb-jsi.so). Drop include ':watermelondb-jsi' and WatermelonDBJSIPackage from the app. Nitro autolinks. Native turbo-sync JSON injection uses com.nozbe.watermelondb.NitromelonNative.provideSyncJson.
  • [Nitro] The NitromelonDatabase HybridObject now exposes the full SQLite adapter API (initialize, find, query, batchJSON, …) as typed Nitrogen methods. The ping() / nativeEngine smoke-test API is removed. iOS/Android no longer install nativeWatermelonCreateAdapter JSI bindings.
  • The npm package is now nitromelondb (was @nozbe/watermelondb). Update install commands and imports (yarn add nitromelondb, import { Database } from 'nitromelondb').

New features

  • [Database] new Database({ experimentalDetectNestedWriters: true }) throws immediately when a reader/writer is called from another without callWriter()/callReader(), instead of deadlocking. Detection covers the same JS turn and the continuation after Watermelon adapter awaits (find / query / batch).
  • [Electron] Added RemoteAdapter so SQLite can run in Electron's main process over IPC (or any serializable transport). Cherry-picked from Nozbe/WatermelonDB#1859 by @feznyng
  • [Expo] First-class Expo support: config plugin (app.plugin.js) for development builds, EAS Build, and EAS Update. Add "nitromelondb" to app.json plugins. Replaces @morrowdigital/watermelondb-expo-plugin (Android JSI wiring is not used; SQLite is Nitro). Optional { "excludeSimArch": true }.

Fixes

  • [LokiJS] Multitab sync issue fix
  • [Android] Added linker flag for building with 16kB page alignment
  • [Android] Generate BuildConfig under AGP 8+ (fixes cannot find symbol: BuildConfig)
  • [TS] make catchError visible to typescript
  • Stop copying the removed native/android-jsi tree in yarn build (package publish was failing with ENOENT)
  • Run @babel/plugin-transform-typescript before class-field plugins so yarn build can compile declare fields

Changes

  • [Nitro] Native SQLite uses a typed NitromelonDatabase HybridObject wrapping the existing C++ Database. react-native-nitro-modules is an optional peer dependency. The Expo SDK 57 app in examples/nitro uses SQLiteAdapter with a notes schema, migrations, and a list UI.

  • Migrated the JS source from Flow + hand-written .d.ts to TypeScript. Implementation under src/ is now TypeScript, including adapters (SQLite, LokiJS, remote). yarn typecheck uses strict, noUnusedLocals, noUnusedParameters, and exactOptionalPropertyTypes, and forbids explicit any. Tests remain JavaScript.

  • ESLint and TypeScript are dedicated required CI jobs on every pull request. Implementation files under src/ must be TypeScript (JavaScript is only allowed in tests). ESLint uses @typescript-eslint/recommended rather than turning core JS rules off by hand.

  • Removed the Flow toolchain: flow-bin, eslint-plugin-flowtype, Babel Flow plugins, .flowconfig, and flow-typed.

  • Metro strips TypeScript for .ts sources, and yarn test:metro-transform guards that path in CI.

  • Updated better-sqlite3 to 13.0.3

  • Support for React Native 0.87 and React 19

  • Added a Migrating from WatermelonDB guide to the docs site

  • Publish docs to GitHub Pages at https://stasdoskalenko.github.io/NitromelonDB/ (docs index: https://stasdoskalenko.github.io/NitromelonDB/docs)

  • [iOS] simdjson is vendored in native/vendor/simdjson and compiled into the NitromelonDB pod. Autolinking is enough; remove any hand-copied pod 'simdjson' Podfile lines.

  • Dropped the @nozbe/simdjson npm dependency. Native builds compile the official amalgamation from this repo.

  • Dropped the @nozbe/sqlite npm dependency. Android and Windows compile the official amalgamation from native/vendor/sqlite; iOS still links the system sqlite3.

Internal

  • Added GitHub Actions release workflows (Prepare Release / Publish Release) with alpha and beta channels. Notes in CHANGELOG-Unreleased.md are rolled into the release automatically. npm publish uses OIDC trusted publishing (no NPM_TOKEN).
  • Updated internal dependencies
  • Updated documentation scripts
  • [CI] Run JavaScript tests on Node.js 24 only
  • [CI] Run iOS tests on macOS 26 / latest stable Xcode / iPhone 17 (iOS 26)
  • [CI] Android tests use JDK 21
  • [CI] Use latest CocoaPods (1.17) without the old 1.15 / xcodeproj / ethon pins
  • Bundle React Native 0.87 with its own Babel preset
  • [CI] Weekly simdjson bump workflow opens a PR when a newer official amalgamation is available (scripts/vendor-simdjson.mjs)
  • [CI] Weekly sqlite bump workflow opens a PR when a newer official amalgamation is available (scripts/vendor-sqlite.mjs)

0.28 - 2025-04-07

BREAKING CHANGES

  • [iOS] Podspec deployment target was bumped from iOS 11 to iOS 12
  • [Android] Installation of Android JSI adapter has changed. To migrate, remove getJSIModulePackage() override in your MainApplication.{java,kt}, and add new WatermelonDBJSIPackage() to getPackages() override instead. See Installation docs for details.

New features

  • Added Database#experimentalIsVerbose option
  • Support for React Native 0.74+

Fixes

  • [ts] Improved LocalStorage type definition
  • [ts] Add missing .d.ts for experimentalFailsafe decorator
  • [migrations] unsafeExecuteSql migration is now validate to ensure it ends with a semicolon (#1811)

Changes

  • Minimum supported Node.js version is now 18.x
  • Improved Model diagnostic errors now always contain table#id of offending record
  • Update better-sqlite3 to 11.x
  • Update sqlite (used by Android in JSI mode) to 3.46.0
  • [docs] Improved Android installation docs
  • [docs] Removed examples from the codebase as they were unmaintained

Internal

  • Update internal dependencies

0.27.1 - 2023-10-15

Fix missing Changelog for 0.27 release

0.27 - 2023-08-29

Highlights

Removed legacy Swift and Kotlin React Native Modules

Following the addition of new Native Modules in 0.26, we're removing the old implementations. We expect this to simplify installation process and remove a ton of compatibility and configuration issues due to Kotlin version mismatchs and the CocoaPods-Swift issues when using use_frameworks! or Expo.

Experimental React Native Windows support

WatermelonDB now has experimental support for React Native Windows. See Installation docs for details.

Introducing Watermelon React

All React/React Native helpers for Watermelon are now available from a new @nozbe/watermelodb/react folder:

  • DatabaseProvider, useDatabase, withDatabase
  • NEW: withObservables - @nozbe/with-observables as a separate package is deprecated, and is now bundled with WatermelonDB
  • NEW: HOC helpers: compose, withHooks
  • NEW: <WithObservables /> component, a component version of withObservables HOC. Useful when a value being observed is localized to a small part of a larger component, because you can effortlessly narrow down which parts of the component are re-rendered when the value changes without having to extract a new component.

Imports from previous @nozbe/watermelondb/DatabaseProvider and @nozbe/watermelondb/hooks folders are deprecated and will be removed in a future version.

Introducing Watermelon Diagnostics

All debug/dev/diagnostics tools for Watermelon are now available from a new @nozbe/watermelondb/diagnostics folder:

  • NEW: censorRaw - takes a RawRecord/DirtyRaw and censors its string values, while preserving IDs, _status, _changed, and numeric/boolean values. Helpful when viewing database contents in context that could expose private user information
  • NEW: diagnoseDatabaseStructure - analyzes database to find inconsistencies, such as orphaned records (belongs_to relations on model that point to records that don't exist) or broken LokiJS database. Use this to find bugs in your data model.
  • NEW: diagnoseSyncConsistency - compares local database with the server version (contents of first/full sync) to find inconsistencies, missing and excess records. Use this to find bugs in your backend sync implementation.

BREAKING CHANGES

  • @nozbe/with-observables is no longer a WatermelonDB dependency. Change your imports to import { withObservables } from '@nozbe/watermelondb/react'

Changes unlikely to cause issues:

  • [iOS] If import WatermelonDB is used in your Swift app (for Turbo sync), remove it and replace with #import <WatermelonDB/WatermelonDB.h> in the bridging header
  • [iOS] If you use _watermelonDBLoggingHook, remove it. No replacement is provided at this time, feel free to contribute if you need this
  • [iOS] If you use -DENABLE_JSLOCK_PERFORMANCE_HACK, remove it. JSLockPerfHack has been non-functional for some time already, and has now been removed. Please file an issue if you relied on it.

Deprecations

  • Imports from @nozbe/watermelondb/DatabaseProvider and @nozbe/watermelondb/hooks. Change to @nozbe/watermelondb/react

New features

  • New @experimentalFailsafe decorator you can apply before @relation/@immutableRelation so that if relation points to a record that does not exist, .fetch()/.observe() yield undefined instead of throwing an error

Fixes

  • [Flow/TS] Improved typing of DatabaseContext
  • Fixed Cannot read property 'getRandomIds' of null. This error occured if native modules were not correctly installed, however the location of the error caused a lot of confusion.

0.26 - 2023-04-28

Highlights

New Native Modules

We're transitioning SQLite adapters for React Native from Kotlin and Swift to Java and Objective-C.

This is only a small part of WatermelonDB, yet is responsible for a disproportionate amount of issues raised, such as Kotlin version conflicts, Expo build failures, CocoaPods use_frameworks! issues. It makes library installation and updates more complicated for users. It complicates maintenance. Swift doesn't play nicely with either React Native's legacy Native Module system, nor can it interact cleanly with C++ (JSI/New Architecture) without going through Objective-C++.

In other words, in the context of a React Native library, the benefit of these modern, nicer to use languages is far outweighed by the downsides. That's why we (@radex & @rozpierog) decided to rewrite the iOS and Android implementations to Objective-C and Java respectively.

0.26 is a transition release, and it contains both implementations. If you find a regression caused by the new bridge, pass {disableNewBridge: true} to new SQLiteAdapter() and file an issue. We plan to remove the old implementation in 0.27 or 0.28 release.

New documentation

We have a brand new documentation page, built with Docusaurus (contributed by @ErickLuizA).

We plan to expand guides, add typing to examples, and add a proper API reference, but we need your help to do this! See: https://github.com/Nozbe/WatermelonDB/issues/1481

BREAKING CHANGES

  • [iOS] You should remove import of WatermelonDB's SupportingFiles/Bridging.h from your app project's Bridging.h. If this removal causes build issues, please file an issue.

  • [iOS] In your Podfile, replace previous WatermelonDB's pod imports with this:

    # Uncomment this line if you're not using auto-linking
    # pod 'WatermelonDB', path: '../node_modules/@nozbe/watermelondb'
    # WatermelonDB dependency
    pod 'simdjson', path: '../node_modules/@nozbe/simdjson', modular_headers: true
  • Removed functions deprecated for 2+ years:

    • Collection.unsafeFetchRecordsWithSQL(). Use .query(Q.unsafeSqlQuery('select * from...')).fetch() instead.
    • Database.action(). Use Database.write() instead.
    • .subAction(). Use .callWriter() instead.
    • @action decorator. Use @writer instead.

Deprecations

New features

  • [Android] Added experimentalUnsafeNativeReuse option to SQLiteAdapter. See src/adapters/sqlite/type.js for more details
  • You can now pass an array to Q.and(conditions), Q.or(conditions), collection.query(conditions), query.extend(conditions) in addition to spreading multiple arguments
  • Added JSDoc comments to many APIs

Fixes

  • Improved resiliency to "Maximum call stack size exceeded" errors
  • [JSI] Improved reliability when reloading RCTBridge
  • [iOS] Fix "range of supported deployment targets" Xcode warning
  • randomId uses better randum number generator
  • Fixed "no such index" when using non-standard schemas and >1k bulk updates
  • Fixes and changes included in @nozbe/with-observables@1.5.0
  • [Flow] query.batch([model, falsy]) no longer raises an error

Performance

  • Warning is now given if a large number of arguments is passed to Q.and, Q.or, Collection.query, Database.batch instead of a single array
  • randomId() is now 2x faster on Chrome, 10x faster on Safari, 2x faster on iOS (Hermes)

Changes

  • randomId: now also generates upper-case letters
  • Simplified CocoaPods/iOS integration
  • Docs improvements: SQLite versions, Flow declarations, Installation
  • Improved diagnostic warnings and errors: JSI, Writer/Reader
  • Remove old diagnostic warnings no longer relevant: multiple Q.on()s, Database, LokiJSAdapter, SQLiteAdapter
  • Updated flow-bin to 0.200. This shouldn't have an impact on you, but could fix or break Flow if you don't have WatermelonDB set to [declarations] mode
  • Updated @babel/runtime to 7.20.13
  • Updated rxjs to 7.8.0
  • Updated sqlite (SQLite used on Android in JSI mode) to 3.40.1
  • Updated simdjson to 3.1.0

Internal

  • Cleaned up QueryDescription, ios folder structure, JSI implementation by splitting them into smaller parts.
  • [Android] [jsi] Simplify CMakeLists
  • Improve release script

0.25.5 - 2023-02-01

  • Fix Android auto-linking

0.25.4 - 2023-01-31

  • [Sync] Improve memory consumption (less likely to get "Maximum callstack exceeded" error)
  • [TypeScript] Fix type of DirtyRaw to { [key: string]: any } (from Object)

0.25.3 - 2023-01-30

  • Fixed TypeError regression

0.25.2 - 2023-01-30

Fixes

  • Fix TypeScript issues (@paulrostorp feat. @enahum)
  • Fix compilation on Kotlin 1.7
  • Fix regression in Sync that could cause Record ID xxx#yyy was sent over the bridge, but it's not cached error

Internal

  • Update internal dependencies
  • Fix Android CI
  • Improve TypeScript CI

0.25.1 - 2023-01-23

  • Fix React Native 0.71+ Android broken build

0.25 - 2023-01-20

Highlights

  • Fix broken build on React Native 0.71+
  • [Expo] Fixes Expo SDK 44+ build errors (@Kudo)
  • [JSI] Fix an issue that sometimes led to crashing app upon database close

BREAKING CHANGES

  • [Query] Q.where(xxx, undefined) will now throw an error. This is a bug fix, since comparing to undefined was never allowed and would either error out or produce a wrong result in some cases. However, it could technically break an app that relied on existing buggy behavior
  • [JSI+Swift] If you use watermelondbProvideSyncJson() native iOS API, you might need to add import WatermelonDB

New features

  • [adapters] Adapter objects can now be distinguished by checking their static adapterType
  • [Query] New Q.includes('foo') query for case-sensitive exact string includes comparison
  • [adapters] Adapter objects now returns dbName
  • [Sync] Replacement Sync - a new advanced sync feature. Server can now send a full dataset (same as during initial sync) and indicate with { experimentalStrategy: 'replacement' } that instead of applying a diff, local database should be replaced with the dataset sent. Local records not present in the changeset will be deleted. However, unlike clearing database and logging in again, unpushed local changes (to records that are kept after replacement) are preserved. This is useful for recovering from a corrupted local database, or as a hack to deal with very large state changes such that server doesn't know how to efficiently send incremental changes and wants to send a full dataset instead. See docs for more details.
  • [Sync] Added onWillApplyRemoteChanges callback

Performance

  • [LokiJS] Updated Loki with some performance improvements
  • [iOS] JSLockPerfHack now works on iOS 15
  • [Sync] Improved performance of processing large pulls
  • Improved @json decorator, now with optional { memo: true } parameter

Changes

  • [Docs] Added additional Android JSI installation step

Fixes

  • [TypeScript] Improve typings: add unsafeExecute method, localStorage property to Database
  • [android] Fixed compilation on some setups due to a missing <cassert> import
  • [sync] Fixed marking changes as synced for users that don't keep globally unique (only per-table unique) IDs
  • Fix Model.experimentalMarkAsDeleted/experimentalDestroyPermanently() throwing an error in some cases
  • Fixes included in updated withObservables

0.24 - 2021-10-28

BREAKING CHANGES

  • Q.experimentalSortBy, Q.experimentalSkip, Q.experimentalTake have been renamed to Q.sortBy, Q.skip, Q.take respectively
  • RxJS has been updated to 7.3.0. If you're not importing from rxjs in your app, this doesn't apply to you. If you are, read RxJS 7 breaking changes: https://rxjs.dev/deprecations/breaking-changes

New features

  • LocalStorage. database.localStorage is now available
  • sortBy, skip, take are now available in LokiJSAdapter as well
  • Disposable records. Read-only records that cannot be saved in the database, updated, or deleted and only exist for as long as you keep a reference to them in memory can now be created using collection.disposableFromDirtyRaw(). This is useful when you're adding online-only features to an otherwise offline-first app.
  • [Sync] experimentalRejectedIds parameter now available in push response to allow partial rejection of an otherwise successful sync

Fixes

  • Fixes an issue when using Headless JS on Android with JSI mode enabled - pass usesExclusiveLocking: true to SQLiteAdapter to enable
  • Fixes Typescript annotations for Collection and adapters/sqlite

0.23 - 2021-07-22

This is a big release to WatermelonDB with new advanced features, great performance improvements, and important fixes to JSI on Android.

Please don't get scared off the long list of breaking changes - they are all either simple Find&Replace renames or changes to internals you probably don't use. It shouldn't take you more than 15 minutes to upgrade to 0.23.

BREAKING CHANGES

  • iOS Installation change. You need to add this line to your Podfile: pod 'simdjson', path: '../node_modules/@nozbe/simdjson'
  • Deprecated new Database({ actionsEnabled: false }) options is now removed. Actions are always enabled.
  • Deprecated new SQLiteAdapter({ synchronous: true }) option is now removed. Use { jsi: true } instead.
  • Deprecated Q.unsafeLokiFilter is now removed. Use Q.unsafeLokiTransform((raws, loki) => raws.filter(raw => ...)) instead.
  • Deprecated Query.hasJoins is now removed
  • Changes to LokiJSAdapter constructor options:
    • indexedDBSerializer -> extraIncrementalIDBOptions: { serializeChunk, deserializeChunk }
    • onIndexedDBFetchStart -> extraIncrementalIDBOptions: { onFetchStart }
    • onIndexedDBVersionChange -> extraIncrementalIDBOptions: { onversionchange }
    • autosave: false -> extraLokiOptions: { autosave: false }
  • Changes to Internal APIs. These were never meant to be public, and so are unlikely to affect you:
    • Model._isCommited, ._hasPendingUpdate, ._hasPendingDelete have been removed and changed to Model._pendingState
    • Collection.unsafeClearCache() is no longer exposed
  • Values passed to adapter.setLocal() are now validated to be strings. This is technically a bug fix, since local storage was always documented to only accept strings, however applications may have relied on this lack of validation. Adding this validation was necessary to achieve consistent behavior between SQLiteAdapter and LokiJSAdapter
  • unsafeSql passed to appSchema will now also be called when dropping and later recreating all database indices on large batches. A second argument was added so you can distinguish between these cases. See Schema docs for more details.
  • Changes to sync change tracking. The behavior of record._raw._changed and record._raw._status (a.k.a. record.syncStatus) has changed. This is unlikely to be a breaking change to you, unless you're writing your own sync engine or rely on these low-level details.
    • Previously, _changed was always empty when _status=created. Now, _changed is not populated during initial creation of a record, but a later update will add changed fields to _changed. This change was necessary to fix a long-standing Sync bug.

Deprecations

  • database.action(() => {}) is now deprecated. Use db.write(() => {}) instead (or db.read(() => {}) if you only need consistency but are not writing any changes to DB)
  • @action is now deprecated. Use @writer or @reader instead
  • .subAction() is now deprecated. Use .callReader() or .callWriter() instead
  • Collection.unsafeFetchRecordsWithSQL() is now deprecated. Use collection.query(Q.unsafeSqlQuery("select * from...")).fetch() instead.

New features

  • db.write(writer => { ... writer.batch() }) - you can now call batch on the interface passed to a writer block
  • Fetching record IDs and unsafe raws. You can now optimize fetching of queries that only require IDs, not full cached records:
    • await query.fetchIds() will return an array of record ids
    • await query.unsafeFetchRaw() will return an array of unsanitized, unsafe raw objects (use alongside Q.unsafeSqlQuery to exclude unnecessary or include extra columns)
    • advanced adapter.queryIds(), adapter.unsafeQueryRaw are also available
  • Raw SQL queries. New syntax for running unsafe raw SQL queries:
    • collection.query(Q.unsafeSqlQuery("select * from tasks where foo = ?", ['bar'])).fetch()
    • You can now also run .fetchCount(), .fetchIds() on SQL queries
    • You can now safely pass values for SQL placeholders by passing an array
    • You can also observe an unsafe raw SQL query -- with some caveats! refer to documentation for more details
  • Unsafe raw execute. You can now execute arbitrary SQL queries (SQLiteAdapter) or access Loki object directly (LokiJSAdapter) using adapter.unsafeExecute -- see docs for more details
  • Turbo Login. You can now speed up the initial (login) sync by up to 5.3x with Turbo Login. See Sync docs for more details.
  • New diagnostic tool - debugPrintChanges. See Sync documentation for more details

Performance

  • The order of Q. clauses in a query is now preserved - previously, the clauses could get rearranged and produce a suboptimal query
  • [SQLite] adapter.batch() with large numbers of created/updated/deleted records is now between 16-48% faster
  • [LokiJS] Querying and finding is now faster - unnecessary data copy is skipped
  • [jsi] 15-30% faster querying on JSC (iOS) when the number of returned records is large
  • [jsi] up to 52% faster batch creation (yes, that's on top of the improvement listed above!)
  • Fixed a performance bug that caused observed items on a list observer with .observeWithColumns() to be unnecessarily re-rendered just before they were removed from the list

Changes

  • All Watermelon console logs are prepended with a 🍉 tag
  • Extra protections against improper use of writers/readers (formerly actions) have been added
  • Queries with multiple top-level Q.on('table', ...) now produce a warning. Use Q.on('table', [condition1, condition2, ...]) syntax instead.
  • [jsi] WAL mode is now used

Fixes

  • [jsi] Fix a race condition where commands sent to the database right after instantiating SQLiteAdapter would fail
  • [jsi] Fix incorrect error reporting on some sqlite errors
  • [jsi] Fix issue where app would crash on Android/Hermes on reload
  • [jsi] Fix IO errors on Android
  • [sync] Fixed a long-standing bug that would cause records that are created before a sync and updated during sync's push to lose their most recent changes on a subsequent sync

Internal

  • Internal changes to SQLiteAdapter:
    • .batch is no longer available on iOS implementation
    • .batch/.batchJSON internal format has changed
    • .getDeletedRecords, destroyDeletedRecords, setLocal, removeLocal is no longer available
  • encoded SQLiteAdapter schema has changed
  • LokiJSAdapter has had many internal changes

0.22 - 2021-05-07

BREAKING CHANGES

  • [SQLite] experimentalUseJSI: true option has been renamed to jsi: true

Deprecations

  • [LokiJS] Q.unsafeLokiFilter is now deprecated and will be removed in a future version. Use Q.unsafeLokiTransform((raws, loki) => raws.filter(raw => ...)) instead.

New features

  • [SQLite] [JSI] jsi: true now works on Android - see docs for installation info

Performance

  • Removed dependency on rambdax and made the util library smaller
  • Faster withObservables

Changes

  • Synchronization: pushChanges is optional, will not calculate local changes if not specified.
  • withObservables is now a dependency of WatermelonDB for simpler installation and consistent updates. You can (and generally should) delete @nozbe/with-observables from your app's package.json
  • [Docs] Add advanced tutorial to share database across iOS targets - @thiagobrez
  • [SQLite] Allowed callbacks (within the migrationEvents object) to be passed so as to track the migration events status ( onStart, onSuccess, onError ) - @avinashlng1080
  • [SQLite] Added a dev-only Query._sql() method for quickly extracting SQL from Queries for debugging purposes

Fixes

  • Non-react statics hoisting in withDatabase()
  • Fixed incorrect reference to process, which can break apps in some environments (e.g. webpack5)
  • [SQLite] [JSI] Fixed JSI mode when running on Hermes
  • Fixed a race condition when using standard fetch methods alongside Collection.unsafeFetchRecordsWithSQL - @jspizziri
  • withObservables shouldn't cause any RxJS issues anymore as it no longer imports RxJS
  • [Typescript] Added onSetUpError and onIndexedDBFetchStart fields to LokiAdapterOptions; fixes TS error - @3DDario
  • [Typescript] Removed duplicated identifiers useWebWorker and useIncrementalIndexedDB in LokiAdapterOptions - @3DDario
  • [Typescript] Fix default export in logger util

0.21 - 2021-03-24

BREAKING CHANGES

  • [LokiJS] useWebWorker and useIncrementalIndexedDB options are now required (previously, skipping them would only trigger a warning)

New features

  • [Model] Model.update method now returns updated record
  • [adapters] onSetUpError: Error => void option is added to both SQLiteAdapter and LokiJSAdapter. Supply this option to catch initialization errors and offer the user to reload or log out
  • [LokiJS] new extraLokiOptions and extraIncrementalIDBOptions options
  • [Android] Autolinking is now supported.
    • If You upgrade to <= v0.21.0 AND are on a version of React Native which supports Autolinking, you will need to remove the config manually linking WatermelonDB.
    • You can resolve this issue by REMOVING the lines of config from your project which are added in the Manual Install ONLY section of the Android Install docs.

Performance

  • [LokiJS] Improved performance of launching the app

Changes

  • [LokiJS] useWebWorker: true and useIncrementalIndexedDB: false options are now deprecated. If you rely on these features, please file an issue!
  • [Sync] Optional log passed to sync now has more helpful diagnostic information
  • [Sync] Open-sourced a simple SyncLogger you can optionally use. See docs for more info.
  • [SQLiteAdapter] synchronous:true option is now deprecated and will be replaced with experimentalUseJSI: true in the future. Please test if your app compiles and works well with experimentalUseJSI: true, and if not - file an issue!
  • [LokiJS] Changed default autosave interval from 250 to 500ms
  • [Typescript] Add experimentalNestedJoin definition and unsafeSqlExpr clause

Fixes

  • [LokiJS] Fixed a case where IndexedDB could get corrupted over time
  • [Resilience] Added extra diagnostics for when you encounter the Record ID aa#bb was sent over the bridge, but it's not cached error and a recovery path (LokiJSAdapter-only). Please file an issue if you encounter this issue!
  • [Typescript] Fixed type on OnFunction to accept and in join
  • [Typescript] Fixed type database#batch(records)'s argument records to accept mixed types

Internal

  • Added an experimental mode where a broken database state is detected, further mutations prevented, and the user notified

0.20 - 2020-10-05

BREAKING CHANGES

This release has unintentionally broken RxJS for some apps using with-observables. If you have this issue, please update @nozbe/with-observables to the latest version.

New features

  • [Sync] Conflict resolution can now be customized. See docs for more details
  • [Android] Autolinking is now supported
  • [LokiJS] Adapter autosave option is now configurable

Changes

  • Interal RxJS imports have been refactor such that rxjs-compat should never be used now
  • [Performance] Tweak Babel config to produce smaller code
  • [Performance] LokiJS-based apps will now take up to 30% less time to load the database (id and unique indicies are generated lazily)

Fixes

  • [iOS] Fixed crash on database reset in apps linked against iOS 14 SDK
  • [LokiJS] Fix Q.like being broken for multi-line strings on web
  • Fixed warn "import cycle" from DialogProvider (#786) by @gmonte.
  • Fixed cache date as instance of Date (#828) by @djorkaeffalexandre.

0.19 - 2020-08-17

New features

  • [iOS] Added CocoaPods support - @leninlin
  • [NodeJS] Introducing a new SQLite Adapter based integration to NodeJS. This requires a peer dependency on better-sqlite3 and should work with the same configuration as iOS/Android - @sidferreira
  • [Android] exerimentalUseJSI option has been enabled on Android. However, it requires some app-specific setup which is not yet documented - stay tuned for upcoming releases
  • [Schema] [Migrations] You can now pass unsafeSql parameters to schema builder and migration steps to modify SQL generated to set up the database or perform migrations. There's also new unsafeExecuteSql migration step. Please use this only if you know what you're doing — you shouldn't need this in 99% of cases. See Schema and Migrations docs for more details
  • [LokiJS] [Performance] Added experimental onIndexedDBFetchStart and indexedDBSerializer options to LokiJSAdapter. These can be used to improve app launch time. See src/adapters/lokijs/index.js for more details.

Changes

  • [Performance] findAndObserve is now able to emit a value synchronously. By extension, this makes Relations put into withObservables able to render the child component in one shot. Avoiding the extra unnecessary render cycles avoids a lot of DOM and React commit-phase work, which can speed up loading some views by 30%
  • [Performance] LokiJS is now faster (refactored encodeQuery, skipped unnecessary clone operations)

0.18 - 2020-06-30

Another WatermelonDB release after just a week? Yup! And it's jam-packed full of features!

New features

  • [Query] Q.on queries are now far more flexible. Previously, they could only be placed at the top level of a query. See Docs for more details. Now, you can:

    • Pass multiple conditions on the related query, like so:

      collection.query(Q.on('projects', [Q.where('foo', 'bar'), Q.where('bar', 'baz')]))
    • You can place Q.on deeper inside the query (nested inside Q.and(), Q.or()). However, you must explicitly list all tables you're joining on at the beginning of a query, using: Q.experimentalJoinTables(['join_table1', 'join_table2']).

    • You can nest Q.on conditions inside Q.on, e.g. to make a condition on a grandchild. To do so, it's required to pass Q.experimentalNestedJoin('parent_table', 'grandparent_table') at the beginning of a query

  • [Query] Q.unsafeSqlExpr() and Q.unsafeLokiExpr() are introduced to allow adding bits of queries that are not supported by the WatermelonDB query language without having to use unsafeFetchRecordsWithSQL(). See docs for more details

  • [Query] Q.unsafeLokiFilter((rawRecord, loki) => boolean) can now be used as an escape hatch to make queries with LokiJSAdapter that are not otherwise possible (e.g. multi-table column comparisons). See docs for more details

Changes

  • [Performance] [LokiJS] Improved performance of queries containing query comparisons on LokiJSAdapter
  • [Docs] Added Contributing guide for Query language improvements
  • [Deprecation] Query.hasJoins is deprecated
  • [DX] Queries with bad associations now show more helpful error message
  • [Query] Counting queries that contain Q.experimentalTake / Q.experimentalSkip is currently broken - previously it would return incorrect results, but now it will throw an error to avoid confusion. Please contribute to fix the root cause!

Fixes

  • [Typescript] Fixed types of Relation

Internal

  • QueryDescription structure has been changed.

0.17.1 - 2020-06-24

  • Fixed broken iOS build - @mlecoq

0.17 - 2020-06-22

New features

  • [Sync] Introducing Migration Syncs - this allows fully consistent synchronization when migrating between schema versions. Previously, there was no mechanism to incrementally fetch all remote changes in new tables and columns after a migration - so local copy was likely inconsistent, requiring a re-login. After adopting migration syncs, Watermelon Sync will request from backend all missing information. See Sync docs for more details.

  • [iOS] Introducing a new native SQLite database integration, rewritten from scratch in C++, based on React Native's JSI (JavaScript Interface). It is to be considered experimental, however we intend to make it the default (and eventually, the only) implementation. In a later release, Android version will be introduced.

    The new adapter is up to 3x faster than the previously fastest synchronous: true option, however this speedup is only achieved with some unpublished React Native patches.

    To try out JSI, add experimentalUseJSI: true to SQLiteAdapter constructor.

  • [Query] Added Q.experimentalSortBy(sortColumn, sortOrder), Q.experimentalTake(count), Q.experimentalSkip(count) methods (only availble with SQLiteAdapter) - @Kenneth-KT

  • Database.batch() can now be called with a single array of models

  • [DX] Database.get(tableName) is now a shortcut for Database.collections.get(tableName)

  • [DX] Query is now thenable - you can now use await query and await query.count instead of await query.fetch() and await query.fetchCount()

  • [DX] Relation is now thenable - you can now use await relation instead of await relation.fetch()

  • [DX] Exposed collection.db and model.db as shortcuts to get to their Database object

Changes

  • [Hardening] Column and table names starting with __, Object property names (e.g. constructor), and some reserved keywords are now forbidden
  • [DX] [Hardening] QueryDescription builder methods do tighter type checks, catching more bugs, and preventing users from unwisely passing unsanitized user data into Query builder methods
  • [DX] [Hardening] Adapters check early if table names are valid
  • [DX] Collection.find reports an error more quickly if an obviously invalid ID is passed
  • [DX] Intializing Database with invalid model classes will now show a helpful error
  • [DX] DatabaseProvider shows a more helpful error if used improperly
  • [Sync] Sync no longer fails if pullChanges returns collections that don't exist on the frontend - shows a warning instead. This is to make building backwards-compatible backends less error-prone
  • [Sync] [Docs] Sync documentation has been rewritten, and is now closer in detail to a formal specification
  • [Hardening] database.collections.get() better validates passed value
  • [Hardening] Prevents unsafe strings from being passed as column name/table name arguments in QueryDescription

Fixes

  • [Sync] Fixed RangeError: Maximum call stack size exceeded when syncing large amounts of data - @leninlin
  • [iOS] Fixed a bug that could cause a database operation to fail with an (6) SQLITE_LOCKED error
  • [iOS] Fixed 'jsi/jsi.h' file not found when building at the consumer level. Added path $(SRCROOT)/../../../../../ios/Pods/Headers/Public/React-jsi to Header Search Paths (issue #691) - @victorbutler
  • [Native] SQLite keywords used as table or column names no longer crash
  • Fixed potential issues when subscribing to database, collection, model, queries passing a subscriber function with the same identity more than once

Internal

  • Fixed broken adapter tests

0.15.1, 0.16.1-fix, 0.16.2 - 2020-06-03

This is a security patch for a vulnerability that could cause maliciously crafted record IDs to cause all or some of user's data to be deleted. More information available via GitHub security advisory

0.16.1 - 2020-05-18

Changes

  • Database.unsafeResetDatabase() is now less unsafe — more application bugs are being caught

Fixes

  • [iOS] Fix build in apps using Flipper
  • [Typescript] Added type definition for setGenerator.
  • [Typescript] Fixed types of decorators.
  • [Typescript] Add Tests to test Types.
  • Fixed typo in learn-to-use docs.
  • [Typescript] Fixed types of changes.

Internal

  • [SQLite] Infrastruture for a future JSI adapter has been added

0.16 - 2020-03-06

⚠️ Breaking

  • experimentalUseIncrementalIndexedDB has been renamed to useIncrementalIndexedDB

Low breakage risk

  • [adapters] Adapter API has changed from returning Promise to taking callbacks as the last argument. This won't affect you unless you call on adapter methods directly. database.adapter returns a new DatabaseAdapterCompat which has the same shape as old adapter API. You can use database.adapter.underlyingAdapter to get back SQLiteAdapter / LokiJSAdapter
  • [Collection] Collection.fetchQuery and Collection.fetchCount are removed. Please use Query.fetch() and Query.fetchCount().

New features

  • [SQLiteAdapter] [iOS] Add new synchronous option to adapter: new SQLiteAdapter({ ..., synchronous: true }). When enabled, database operations will block JavaScript thread. Adapter actions will resolve in the next microtask, which simplifies building flicker-free interfaces. Adapter will fall back to async operation when synchronous adapter is not available (e.g. when doing remote debugging)
  • [LokiJS] Added new onQuotaExceededError?: (error: Error) => void option to LokiJSAdapter constructor. This is called when underlying IndexedDB encountered a quota exceeded error (ran out of allotted disk space for app) This means that app can't save more data or that it will fall back to using in-memory database only Note that this only works when useWebWorker: false

Changes

  • [Performance] Watermelon internals have been rewritten not to rely on Promises and allow some fetch/observe calls to resolve synchronously. Do not rely on this -- external API is still based on Rx and Promises and may resolve either asynchronously or synchronously depending on capabilities. This is meant as a internal performance optimization only for the time being.
  • [LokiJS] [Performance] Improved worker queue implementation for performance
  • [observation] Refactored observer implementations for performance

Fixes

  • Fixed a possible cause for "Record ID xxx#yyy was sent over the bridge, but it's not cached" error
  • [LokiJS] Fixed an issue preventing database from saving when using experimentalUseIncrementalIndexedDB
  • Fixed a potential issue when using database.unsafeResetDatabase()
  • [iOS] Fixed issue with clearing database under experimental synchronous mode

New features (Experimental)

  • [Model] Added experimental model.experimentalSubscribe((isDeleted) => { ... }) method as a vanilla JS alternative to Rx based model.observe(). Unlike the latter, it does not notify the subscriber immediately upon subscription.
  • [Collection] Added internal collection.experimentalSubscribe((changeSet) => { ... }) method as a vanilla JS alternative to Rx based collection.changes (you probably shouldn't be using this API anyway)
  • [Database] Added experimental database.experimentalSubscribe(['table1', 'table2'], () => { ... }) method as a vanilla JS alternative to Rx-based database.withChangesForTables(). Unlike the latter, experimentalSubscribe notifies the subscriber only once after a batch that makes a change in multiple collections subscribed to. It also doesn't notify the subscriber immediately upon subscription, and doesn't send details about the changes, only a signal.
  • Added experimentalDisableObserveCountThrottling() to @nozbe/watermelondb/observation/observeCount that globally disables count observation throttling. We think that throttling on WatermelonDB level is not a good feature and will be removed in a future release - and will be better implemented on app level if necessary
  • [Query] Added experimental query.experimentalSubscribe(records => { ... }), query.experimentalSubscribeWithColumns(['col1', 'col2'], records => { ... }), and query.experimentalSubscribeToCount(count => { ... }) methods

0.15 - 2019-11-08

Highlights

This is a massive new update to WatermelonDB! 🍉

  • Up to 23x faster sync. You heard that right. We've made big improvements to performance. In our tests, with a massive sync (first login, 45MB of data / 65K records) we got a speed up of:

    • 5.7s -> 1.2s on web (5x)
    • 142s -> 6s on iOS (23x)

    Expect more improvements in the coming releases!

  • Improved LokiJS adapter. Option to disable web workers, important Safari 13 fix, better performance, and now works in Private Modes. We recommend adding useWebWorker: false, experimentalUseIncrementalIndexedDB: true options to the LokiJSAdapter constructor to take advantage of the improvements, but please read further changelog to understand the implications of this.

  • Raw SQL queries now available on iOS and Android thanks to the community

  • Improved TypeScript support — thanks to the community

⚠️ Breaking

  • Deprecated bool schema column type is removed -- please change to boolean
  • Experimental experimentalSetOnlyMarkAsChangedIfDiffers(false) API is now removed

New featuers

  • [Collection] Add Collection.unsafeFetchRecordsWithSQL() method. You can use it to fetch record using raw SQL queries on iOS and Android. Please be careful to avoid SQL injection and other pitfalls of raw queries

  • [LokiJS] Introduces new new LokiJSAdapter({ ..., experimentalUseIncrementalIndexedDB: true }) option. When enabled, database will be saved to browser's IndexedDB using a new adapter that only saves the changed records, instead of the entire database.

    This works around a serious bug in Safari 13 (https://bugs.webkit.org/show_bug.cgi?id=202137) that causes large databases to quickly balloon to gigabytes of temporary trash

    This also improves performance of incremental saves, although initial page load or very, very large saves might be slightly slower.

    This is intended to become the new default option, but it's not backwards compatible (if enabled, old database will be lost). You're welcome to contribute an automatic migration code.

    Note that this option is still experimental, and might change in breaking ways at any time.

  • [LokiJS] Introduces new new LokiJSAdapter({ ..., useWebWorker: false }) option. Before, web workers were always used with LokiJSAdapter. Although web workers may have some performance benefits, disabling them may lead to lower memory consumption, lower latency, and easier debugging. YMMV.

  • [LokiJS] Added onIndexedDBVersionChange option to LokiJSAdapter. This is a callback that's called when internal IDB version changed (most likely the database was deleted in another browser tab). Pass a callback to force log out in this copy of the app as well. Note that this only works when using incrementalIDB and not using web workers

  • [Model] Add Model._dangerouslySetRawWithoutMarkingColumnChange() method. You probably shouldn't use it, but if you know what you're doing and want to live-update records from server without marking record as updated, this is useful

  • [Collection] Add Collection.prepareCreateFromDirtyRaw()

  • @json decorator sanitizer functions take an optional second argument, with a reference to the model

Fixes

  • Pinned required rambdax version to 2.15.0 to avoid console logging bug. In a future release we will switch to our own fork of rambdax to avoid future breakages like this.

Improvements

  • [Performance] Make large batches a lot faster (1.3s shaved off on a 65K insert sample)
  • [Performance] [iOS] Make large batch inserts an order of magnitude faster
  • [Performance] [iOS] Make encoding very large queries (with thousands of parameters) 20x faster
  • [Performance] [LokiJS] Make batch inserts faster (1.5s shaved off on a 65K insert sample)
  • [Performance] [LokiJS] Various performance improvements
  • [Performance] [Sync] Make Sync faster
  • [Performance] Make observation faster
  • [Performance] [Android] Make batches faster
  • Fix app glitches and performance issues caused by race conditions in Query.observeWithColumns()
  • [LokiJS] Persistence adapter will now be automatically selected based on availability. By default, IndexedDB is used. But now, if unavailable (e.g. in private mode), ephemeral memory adapter will be used.
  • Disabled console logs regarding new observations (it never actually counted all observations) and time to query/count/batch (the measures were wildly inaccurate because of asynchronicity - actual times are much lower)
  • [withObservables] Improved performance and debuggability (update withObservables package separately)
  • Improved debuggability of Watermelon -- shortened Rx stacks and added function names to aid in understanding call stacks and profiles
  • [adapters] The adapters interface has changed. query() and count() methods now receive a SerializedQuery, and batch() now takes TableName<any> and RawRecord or RecordId instead of Model.
  • [Typescript] Typing improvements
    • Added 3 missing properties collections, database and asModel in Model type definition.
    • Removed optional flag on actionsEnabled in the Database constructor options since its mandatory since 0.13.0.
    • fixed several further typing issues in Model, Relation and lazy decorator
  • Changed how async functions are transpiled in the library. This could break on really old Android phones but shouldn't matter if you use latest version of React Native. Please report an issue if you see a problem.
  • Avoid database prop drilling in the web demo

0.14.1 - 2019-08-31

Hotfix for rambdax crash

0.14.0 - 2019-08-02

New features

  • [Query] Added support for notLike queries 🎉
  • [Actions] You can now batch delete record with all descendants using experimental functions experimentalMarkAsDeleted or experimentalDestroyPermanently

0.13.0 - 2019-07-18

⚠️ Breaking

  • [Database] It is now mandatory to pass actionsEnabled: option to Database constructor. It is recommended that you enable this option:

    const database = new Database({
    adapter: ...,
    modelClasses: [...],
    actionsEnabled: true
    })

    See docs/Actions.md for more details about Actions. You can also pass false to maintain backward compatibility, but this option will be removed in a later version

  • [Adapters] migrationsExperimental prop of SQLiteAdapter and LokiJSAdapter has been renamed to migrations.

New features

  • [Actions] You can now batch deletes by using prepareMarkAsDeleted or prepareDestroyPermanently
  • [Sync] Performance: synchronize() no longer calls your pushChanges() function if there are no local changes to push. This is meant to save unnecessary network bandwidth. ⚠️ Note that this could be a breaking change if you rely on it always being called
  • [Sync] When setting new values to fields on a record, the field (and record) will no longer be marked as changed if the field's value is the same. This is meant to improve performance and avoid unnecessary code in the app. ⚠️ Note that this could be a breaking change if you rely on the old behavior. For now you can import experimentalSetOnlyMarkAsChangedIfDiffers from @nozbe/watermelondb/Model/index and call if with (false) to bring the old behavior back, but this will be removed in the later version -- create a new issue explaining why you need this
  • [Sync] Small perf improvements

Improvements

  • [Typescript] Improved types for SQLite and LokiJS adapters, migrations, models, the database and the logger.

0.12.3 - 2019-05-06

Changes

  • [Database] You can now update the random id schema by importing import { setGenerator } from '@nozbe/watermelondb/utils/common/randomId' and then calling setGenerator(newGenenerator). This allows WatermelonDB to create specific IDs for example if your backend uses UUIDs.
  • [Typescript] Type improvements to SQLiteAdapter and Database
  • [Tests] remove cleanup for react-hooks-testing-library@0.5.0 compatibility

0.12.2 - 2019-04-19

Fixes

  • [TypeScript] 'Cannot use 'in' operator to search for 'initializer'; decorator fix

Changes

  • [Database] You can now pass falsy values to Database.batch(...) (false, null, undefined). This is useful in keeping code clean when doing operations conditionally. (Also works with model.batch(...))
  • [Decorators]. You can now use @action on methods of any object that has a database: Database property, and @field @children @date @relation @immutableRelation @json @text @nochange decorators on any object with a asModel: Model property.
  • [Sync] Adds a temporary/experimental _unsafeBatchPerCollection: true flag to synchronize(). This causes server changes to be committed to database in multiple batches, and not one. This is NOT preferred for reliability and performance reasons, but it works around a memory issue that might cause your app to crash on very large syncs (>20,000 records). Use this only if necessary. Note that this option might be removed at any time if a better solution is found.

0.12.1 - 2019-04-01

⚠️ Hotfix

Changes

  • [Sync] Adds basic sync logging capability to Sync. Pass an empty object to synchronize() to populate it with diagnostic information:
    const log = {}
    await synchronize({ database, log, ...})
    console.log(log.startedAt)
    See Sync documentation for more details.

0.12.0 - 2019-03-18

Added

  • [Hooks] new useDatabase hook for consuming the Database Context:
    import { useDatabase } from '@nozbe/watermelondb/hooks'
    const Component = () => {
    const database = useDatabase()
    }
  • [TypeScript] added .d.ts files. Please note: TypeScript definitions are currently incomplete and should be used as a guide only. PRs for improvements would be greatly appreciated!

Performance

  • Improved UI performance by consolidating multiple observation emissions into a single per-collection batch emission when doing batch changes

0.11.0 - 2019-03-12

Breaking

  • ⚠️ Potentially BREAKING fix: a @date field now returns a Jan 1, 1970 date instead of null if the field's raw value is 0. This is considered a bug fix, since it's unexpected to receive a null from a getter of a field whose column schema doesn't say isOptional: true. However, if you relied on this behavior, this might be a breaking change.
  • ⚠️ BREAKING: Database.unsafeResetDatabase() now requires that you run it inside an Action

Bug fixes

  • [Sync] Fixed an issue where synchronization would continue running despite unsafeResetDatabase being called
  • [Android] fix compile error for kotlin 1.3+

Other changes

  • Actions are now aborted when unsafeResetDatabase() is called, making reseting database a little bit safer
  • Updated demo dependencies
  • LokiJS is now a dependency of WatermelonDB (although it's only required for use on the web)
  • [Android] removed unused test class
  • [Android] updated ktlint to 0.30.0

0.10.1 - 2019-02-12

Changes

  • [Android] Changed compile to implementation in Library Gradle file
    • ⚠️ might break build if you are using Android Gradle Plugin <3.X
  • Updated peerDependency react-native to 0.57.0
  • [Sync] Added hasUnsyncedChanges() helper method
  • [Sync] Improved documentation for backends that can't distinguish between created and updated records
  • [Sync] Improved diagnostics / protection against edge cases
  • [iOS] Add missing header search path to support ejected expo project.
  • [Android] Fix crash on android < 5.0
  • [iOS] SQLiteAdapter's dbName path now allows you to pass an absolute path to a file, instead of a name
  • [Web] Add adaptive layout for demo example with smooth scrolling for iOS

0.10.0 - 2019-01-18

Breaking

  • BREAKING: Table column last_modified is no longer automatically added to all database tables. If you don't use this column (e.g. in your custom sync code), you don't have to do anything. If you do, manually add this column to all table definitions in your Schema:
    { name: 'last_modified', type: 'number', isOptional: true }
    Don't bump schema version or write a migration for this.

New

  • Actions API.

    This was actually released in 0.8.0 but is now documented. With Actions enabled, all create/update/delete/batch calls must be wrapped in an Action.

    To use Actions, call await database.action(async () => { /* perform writes here */ }, and in Model instance methods, you can just decorate the whole method with @action.

    This is necessary for Watermelon Sync, and also to enable greater safety and consistency.

    To enable actions, add actionsEnabled: true to new Database({ ... }). In a future release this will be enabled by default, and later, made mandatory.

    See documentation for more details.

  • Watermelon Sync Adapter (Experimental)

    Added synchronize() function that allows you to easily add full synchronization capabilities to your Watermelon app. You only need to provide two fetch calls to your remote server that conforms to Watermelon synchronization protocol, and all the client-side processing (applying remote changes, resolving conflicts, finding local changes, and marking them as synced) is done by Watermelon.

    See documentation for more details.

  • Support caching for non-global IDs at Native level

0.9.0 - 2018-11-23

New

  • Added Q.like - you can now make queries similar to SQL LIKE

0.8.0 - 2018-11-16

New

  • Added DatabaseProvider and withDatabase Higher-Order Component to reduce prop drilling
  • Added experimental Actions API. This will be documented in a future release.

Fixes

  • Fixes crash on older Android React Native targets without jsc-android installed

0.7.0 - 2018-10-31

Deprecations

  • [Schema] Column type 'bool' is deprecated — change to 'boolean'

New

  • Added support for Schema Migrations. See documentation for more details.
  • Added fundaments for integration of Danger with Jest

Changes

  • Fixed "dependency cycle" warning
  • [SQLite] Fixed rare cases where database could be left in an unusable state (added missing transaction)
  • [Flow] Fixes oneOf() typing and some other variance errors
  • [React Native] App should launch a little faster, because schema is only compiled on demand now
  • Fixed typos in README.md
  • Updated Flow to 0.85

0.6.2 - 2018-10-04

Deprecations

  • The @nozbe/watermelondb/babel/cjs / @nozbe/watermelondb/babel/esm Babel plugin that ships with Watermelon is deprecated and no longer necessary. Delete it from your Babel config as it will be removed in a future update

Refactoring

  • Removed dependency on async (Web Worker should be ~30KB smaller)
  • Refactored Collection and simpleObserver for getting changes in an array and also adds CollectionChangeTypes for differentiation between different changes
  • Updated dependencies
  • Simplified build system by using relative imports
  • Simplified build package by outputting CJS-only files

0.6.1 - 2018-09-20

Added

  • Added iOS and Android integration tests and lint checks to TravisCI

Changed

  • Changed Flow setup for apps using Watermelon - see docs/Advanced/Flow.md
  • Improved documentation, and demo code
  • Updated dependencies

Fixed

  • Add quotes to all names in sql queries to allow keywords as table or column names
  • Fixed running model tests in apps with Watermelon in the loop
  • Fixed Flow when using Watermelon in apps

0.6.0 - 2018-09-05

Initial release of WatermelonDB