@rikishi/watermelondb
Version:
Build powerful React Native and React web apps that scale from hundreds to tens of thousands of records and remain fast
1 lines • 1.32 MB
JSON
{"doc_urls":["ch01-00-get-excited.html#get-excited","index.html#why-watermelon","index.html#usage","index.html#--learn-more---see-full-documentation","index.html#who-uses-watermelondb","index.html#contributing","index.html#author-and-license","Demo.html#demo","Demo.html#online-demo","Demo.html#running-react-native-demo","Demo.html#running-web-demo","ch02-00-learn-to-use.html#learn-to-use-watermelon","Installation.html#installation","Installation.html#react-native-setup","Installation.html#ios-react-native","Installation.html#android-react-native","Installation.html#web-setup","Installation.html#nodejs-sqlite-setup","Installation.html#next-steps","Setup.html#set-up-your-app-for-watermelondb","Setup.html#next-steps","Schema.html#schema","Schema.html#defining-a-schema","Schema.html#column-types","Schema.html#naming-conventions","Schema.html#special-columns","Schema.html#modifying-schema","Schema.html#indexing","Schema.html#advanced","Schema.html#unsafe-sql-schema","Schema.html#next-steps","Model.html#defining-models","Model.html#create-a-model","Model.html#associations","Model.html#add-fields","Model.html#date-fields","Model.html#derived-fields","Model.html#to-one-relation-fields","Model.html#children-to-many-relation-fields","Model.html#custom-queries","Model.html#writer-methods","Model.html#advanced-fields","Model.html#next-steps","CRUD.html#create-read-update-delete","CRUD.html#reading","CRUD.html#modifying-the-database","CRUD.html#create-a-new-record","CRUD.html#update-a-record","CRUD.html#delete-a-record","CRUD.html#advanced","CRUD.html#advanced-unsafe-raw-execute","CRUD.html#next-steps","Components.html#connecting-to-components","Components.html#reactive-components","Components.html#reactive-lists","Components.html#reactive-relations","Components.html#reactive-counters","Components.html#hey-what-about-react-hooks","Components.html#understanding-withobservables","Components.html#advanced","Components.html#advanced-observing-sorted-lists","Components.html#advanced-observing-2nd-level-relations","Components.html#database-provider","Components.html#usedatabase","Components.html#next-steps","Query.html#query-api","Query.html#defining-queries","Query.html#children","Query.html#extended-query","Query.html#custom-queries","Query.html#executing-queries","Query.html#query-conditions","Query.html#conditions-with-other-operators","Query.html#andor-nesting","Query.html#conditions-on-related-tables-join-queries","Query.html#advanced-queries","Query.html#advanced-observing","Query.html#column-comparisons","Query.html#sortby-take-skip","Query.html#fetch-ids","Query.html#security","Query.html#unsafe-sql-queries","Query.html#unsafe-fetch-raw","Query.html#unsafe-sqlloki-expressions","Query.html#multi-table-column-comparisons-and-qunsafelokitransform","Query.html#null-behavior","Query.html#contributing-improvements-to-watermelon-query-language","Query.html#next-steps","Relation.html#relations","Relation.html#defining-relations","Relation.html#immutablerelation","Relation.html#relation-api","Relation.html#observing","Relation.html#fetching","Relation.html#id","Relation.html#assigning","Relation.html#advanced-relations","Relation.html#many-to-many-relation","Relation.html#next-steps","Writers.html#writers-readers-and-batching","Writers.html#inline-writers","Writers.html#writer-methods","Writers.html#batch-updates","Writers.html#delete-action","Writers.html#advanced-why-are-readers-and-writers-necessary","Writers.html#advanced-readers","Writers.html#advanced-nesting-writers-or-readers","Writers.html#next-steps","ch03-00-advanced.html#advanced-guides","Advanced/Migrations.html#migrations","Advanced/Migrations.html#migrations-setup","Advanced/Migrations.html#migrations-workflow","Advanced/Migrations.html#step-1-add-a-new-migration","Advanced/Migrations.html#step-2-make-matching-changes-in-schema","Advanced/Migrations.html#step-3-bump-schema-version","Advanced/Migrations.html#step-4-test-your-migrations","Advanced/Migrations.html#why-is-this-order-important","Advanced/Migrations.html#migrations-api","Advanced/Migrations.html#migration-steps","Advanced/Migrations.html#database-reseting-and-other-edge-cases","Advanced/Migrations.html#rolling-back-changes","Advanced/Migrations.html#unsafe-sql-migrations","Advanced/Sync.html#synchronization","Advanced/Sync.html#using-synchronize-in-your-app","Advanced/Sync.html#troubleshooting","Advanced/Sync.html#implementing-pullchanges","Advanced/Sync.html#implementing-pushchanges","Advanced/Sync.html#checking-unsynced-changes","Advanced/Sync.html#general-information-and-tips","Advanced/Sync.html#adopting-migration-syncs","Advanced/Sync.html#adopting-turbo-login","Advanced/Sync.html#adding-logging-to-your-sync","Advanced/Sync.html#debugging-changes","Advanced/Sync.html#additional-synchronize-flags","Advanced/Sync.html#implementing-your-sync-backend","Advanced/Sync.html#understanding-changes-objects","Advanced/Sync.html#implementing-pull-endpoint","Advanced/Sync.html#implementing-push-endpoint","Advanced/Sync.html#tips-on-implementing-server-side-changes-tracking","Advanced/Sync.html#local-vs-remote-ids","Advanced/Sync.html#existing-backend-implementations-for-watermelondb","Advanced/Sync.html#current-sync-limitations","Advanced/Sync.html#contributing","Advanced/Sync.html#sync-primitives-and-implementing-your-own-sync-entirely-from-scratch","Advanced/CreateUpdateTracking.html#createupdate-tracking","Advanced/CreateUpdateTracking.html#when-to-use-this","Advanced/CreateUpdateTracking.html#how-to-do-this","Advanced/CreateUpdateTracking.html#how-this-behaves","Advanced/AdvancedFields.html#advanced-fields","Advanced/AdvancedFields.html#json","Advanced/AdvancedFields.html#nochange","Advanced/AdvancedFields.html#readonly","Advanced/AdvancedFields.html#custom-observable-fields","Advanced/Flow.html#watermelon--flow","Advanced/Flow.html#setup","Advanced/Flow.html#tables-and-columns","Advanced/Flow.html#but-isnt-that-a-lot-of-boilerplate","Advanced/Flow.html#associations","Advanced/Flow.html#common-types","Advanced/LocalStorage.html#local-storage","Advanced/ProTips.html#various-pro-tips","Advanced/ProTips.html#database-viewer","Advanced/ProTips.html#prepopulating-database-on-native","Advanced/Performance.html#performance","Advanced/SharingDatabaseAcrossTargets.html#ios---sharing-database-across-targets","Advanced/SharingDatabaseAcrossTargets.html#when-to-use-this","Advanced/SharingDatabaseAcrossTargets.html#how-to-do-this","ch04-00-deeper.html#dig-deeper-into-watermelondb","Implementation/Architecture.html#architecture","Implementation/Architecture.html#base-objects","Implementation/Architecture.html#helper-functions","Implementation/Adapters.html#database-adapters","Implementation/Adapters.html#react-native","Implementation/Adapters.html#web","Implementation/Adapters.html#writing-your-own-adapter","Implementation/SyncImpl.html#sync-implementation-details","Implementation/SyncImpl.html#implementing-your-own-sync-from-scratch","Implementation/SyncImpl.html#watermelon-sync----details","Implementation/SyncImpl.html#general-design","Implementation/SyncImpl.html#sync-procedure","Implementation/SyncImpl.html#notes","Implementation/SyncImpl.html#migration-syncs","Implementation/SyncImpl.html#reference","ch04-00-deeper.html#dig-deeper-into-watermelondb","Roadmap.html#watermelondb-roadmap","Roadmap.html#v10","Roadmap.html#beyond-10","CONTRIBUTING.html#contributing-guidelines","CONTRIBUTING.html#before-you-send-a-pull-request","CONTRIBUTING.html#running-watermelon-in-development","CONTRIBUTING.html#download-source-and-dependencies","CONTRIBUTING.html#developing-watermelon-alongside-your-app","CONTRIBUTING.html#running-tests","CONTRIBUTING.html#editing-files","CONTRIBUTING.html#editing-native-code","CONTRIBUTING.html#integration-tests","CONTRIBUTING.html#running-tests-manualy","CONTRIBUTING.html#native-linting","CONTRIBUTING.html#native-code-troubleshooting","CHANGELOG.html#changelog","CHANGELOG.html#024---2021-10-28","CHANGELOG.html#breaking-changes","CHANGELOG.html#new-features","CHANGELOG.html#fixes","CHANGELOG.html#023---2021-07-22","CHANGELOG.html#breaking-changes","CHANGELOG.html#deprecations","CHANGELOG.html#new-features","CHANGELOG.html#performance","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#internal","CHANGELOG.html#022---2021-05-07","CHANGELOG.html#breaking-changes","CHANGELOG.html#deprecations","CHANGELOG.html#new-features","CHANGELOG.html#performance","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#021---2021-03-24","CHANGELOG.html#breaking-changes","CHANGELOG.html#new-features","CHANGELOG.html#performance","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#internal","CHANGELOG.html#020---2020-10-05","CHANGELOG.html#breaking-changes","CHANGELOG.html#new-features","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#019---2020-08-17","CHANGELOG.html#new-features","CHANGELOG.html#changes","CHANGELOG.html#018---2020-06-30","CHANGELOG.html#new-features","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#internal","CHANGELOG.html#0171---2020-06-24","CHANGELOG.html#017---2020-06-22","CHANGELOG.html#new-features","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#internal","CHANGELOG.html#0151-0161-fix-0162---2020-06-03","CHANGELOG.html#0161---2020-05-18","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#internal","CHANGELOG.html#016---2020-03-06","CHANGELOG.html#-breaking","CHANGELOG.html#new-features","CHANGELOG.html#changes","CHANGELOG.html#fixes","CHANGELOG.html#new-features-experimental","CHANGELOG.html#015---2019-11-08","CHANGELOG.html#highlights","CHANGELOG.html#-breaking","CHANGELOG.html#new-featuers","CHANGELOG.html#fixes","CHANGELOG.html#improvements","CHANGELOG.html#0141---2019-08-31","CHANGELOG.html#0140---2019-08-02","CHANGELOG.html#new-features","CHANGELOG.html#0130---2019-07-18","CHANGELOG.html#-breaking","CHANGELOG.html#new-features","CHANGELOG.html#improvements","CHANGELOG.html#0123---2019-05-06","CHANGELOG.html#changes","CHANGELOG.html#0122---2019-04-19","CHANGELOG.html#fixes","CHANGELOG.html#changes","CHANGELOG.html#0121---2019-04-01","CHANGELOG.html#-hotfix","CHANGELOG.html#changes","CHANGELOG.html#0120---2019-03-18","CHANGELOG.html#added","CHANGELOG.html#performance","CHANGELOG.html#0110---2019-03-12","CHANGELOG.html#breaking","CHANGELOG.html#bug-fixes","CHANGELOG.html#other-changes","CHANGELOG.html#0101---2019-02-12","CHANGELOG.html#changes","CHANGELOG.html#0100---2019-01-18","CHANGELOG.html#breaking","CHANGELOG.html#new","CHANGELOG.html#090---2018-11-23","CHANGELOG.html#new","CHANGELOG.html#080---2018-11-16","CHANGELOG.html#new","CHANGELOG.html#fixes","CHANGELOG.html#070---2018-10-31","CHANGELOG.html#deprecations","CHANGELOG.html#new","CHANGELOG.html#changes","CHANGELOG.html#062---2018-10-04","CHANGELOG.html#deprecations","CHANGELOG.html#refactoring","CHANGELOG.html#061---2018-09-20","CHANGELOG.html#added","CHANGELOG.html#changed","CHANGELOG.html#fixed","CHANGELOG.html#060---2018-09-05"],"index":{"documentStore":{"docInfo":{"0":{"body":0,"breadcrumbs":2,"title":1},"1":{"body":203,"breadcrumbs":5,"title":1},"10":{"body":41,"breadcrumbs":6,"title":3},"100":{"body":47,"breadcrumbs":8,"title":2},"101":{"body":103,"breadcrumbs":8,"title":2},"102":{"body":151,"breadcrumbs":8,"title":2},"103":{"body":57,"breadcrumbs":8,"title":2},"104":{"body":154,"breadcrumbs":10,"title":4},"105":{"body":79,"breadcrumbs":8,"title":2},"106":{"body":60,"breadcrumbs":10,"title":4},"107":{"body":13,"breadcrumbs":8,"title":2},"108":{"body":4,"breadcrumbs":4,"title":2},"109":{"body":28,"breadcrumbs":4,"title":1},"11":{"body":4,"breadcrumbs":6,"title":3},"110":{"body":35,"breadcrumbs":5,"title":2},"111":{"body":12,"breadcrumbs":5,"title":2},"112":{"body":95,"breadcrumbs":8,"title":5},"113":{"body":80,"breadcrumbs":9,"title":6},"114":{"body":37,"breadcrumbs":8,"title":5},"115":{"body":37,"breadcrumbs":7,"title":4},"116":{"body":36,"breadcrumbs":5,"title":2},"117":{"body":54,"breadcrumbs":5,"title":2},"118":{"body":35,"breadcrumbs":5,"title":2},"119":{"body":85,"breadcrumbs":7,"title":4},"12":{"body":11,"breadcrumbs":5,"title":1},"120":{"body":48,"breadcrumbs":6,"title":3},"121":{"body":27,"breadcrumbs":6,"title":3},"122":{"body":63,"breadcrumbs":4,"title":1},"123":{"body":108,"breadcrumbs":6,"title":3},"124":{"body":42,"breadcrumbs":4,"title":1},"125":{"body":97,"breadcrumbs":5,"title":2},"126":{"body":80,"breadcrumbs":5,"title":2},"127":{"body":20,"breadcrumbs":6,"title":3},"128":{"body":74,"breadcrumbs":6,"title":3},"129":{"body":178,"breadcrumbs":6,"title":3},"13":{"body":45,"breadcrumbs":7,"title":3},"130":{"body":308,"breadcrumbs":6,"title":3},"131":{"body":95,"breadcrumbs":6,"title":3},"132":{"body":56,"breadcrumbs":5,"title":2},"133":{"body":60,"breadcrumbs":6,"title":3},"134":{"body":0,"breadcrumbs":6,"title":3},"135":{"body":85,"breadcrumbs":6,"title":3},"136":{"body":373,"breadcrumbs":6,"title":3},"137":{"body":265,"breadcrumbs":6,"title":3},"138":{"body":258,"breadcrumbs":9,"title":6},"139":{"body":152,"breadcrumbs":7,"title":4},"14":{"body":143,"breadcrumbs":7,"title":3},"140":{"body":21,"breadcrumbs":7,"title":4},"141":{"body":86,"breadcrumbs":6,"title":3},"142":{"body":55,"breadcrumbs":4,"title":1},"143":{"body":4,"breadcrumbs":9,"title":6},"144":{"body":29,"breadcrumbs":7,"title":2},"145":{"body":53,"breadcrumbs":6,"title":1},"146":{"body":45,"breadcrumbs":5,"title":0},"147":{"body":32,"breadcrumbs":6,"title":1},"148":{"body":0,"breadcrumbs":6,"title":2},"149":{"body":200,"breadcrumbs":5,"title":1},"15":{"body":229,"breadcrumbs":7,"title":3},"150":{"body":38,"breadcrumbs":5,"title":1},"151":{"body":19,"breadcrumbs":5,"title":1},"152":{"body":226,"breadcrumbs":7,"title":3},"153":{"body":15,"breadcrumbs":5,"title":2},"154":{"body":33,"breadcrumbs":4,"title":1},"155":{"body":121,"breadcrumbs":5,"title":2},"156":{"body":52,"breadcrumbs":6,"title":3},"157":{"body":36,"breadcrumbs":4,"title":1},"158":{"body":97,"breadcrumbs":5,"title":2},"159":{"body":106,"breadcrumbs":5,"title":2},"16":{"body":88,"breadcrumbs":6,"title":2},"160":{"body":0,"breadcrumbs":7,"title":3},"161":{"body":42,"breadcrumbs":6,"title":2},"162":{"body":41,"breadcrumbs":7,"title":3},"163":{"body":3,"breadcrumbs":5,"title":1},"164":{"body":18,"breadcrumbs":10,"title":4},"165":{"body":17,"breadcrumbs":7,"title":1},"166":{"body":286,"breadcrumbs":6,"title":0},"167":{"body":14,"breadcrumbs":6,"title":3},"168":{"body":0,"breadcrumbs":5,"title":1},"169":{"body":149,"breadcrumbs":6,"title":2},"17":{"body":29,"breadcrumbs":7,"title":3},"170":{"body":53,"breadcrumbs":6,"title":2},"171":{"body":42,"breadcrumbs":6,"title":2},"172":{"body":49,"breadcrumbs":6,"title":2},"173":{"body":65,"breadcrumbs":5,"title":1},"174":{"body":15,"breadcrumbs":6,"title":2},"175":{"body":18,"breadcrumbs":8,"title":3},"176":{"body":91,"breadcrumbs":8,"title":3},"177":{"body":0,"breadcrumbs":8,"title":3},"178":{"body":112,"breadcrumbs":7,"title":2},"179":{"body":202,"breadcrumbs":7,"title":2},"18":{"body":4,"breadcrumbs":6,"title":2},"180":{"body":102,"breadcrumbs":6,"title":1},"181":{"body":161,"breadcrumbs":7,"title":2},"182":{"body":17,"breadcrumbs":6,"title":1},"183":{"body":14,"breadcrumbs":3,"title":3},"184":{"body":44,"breadcrumbs":3,"title":2},"185":{"body":7,"breadcrumbs":2,"title":1},"186":{"body":6,"breadcrumbs":3,"title":2},"187":{"body":0,"breadcrumbs":3,"title":2},"188":{"body":31,"breadcrumbs":5,"title":4},"189":{"body":0,"breadcrumbs":4,"title":3},"19":{"body":264,"breadcrumbs":8,"title":4},"190":{"body":6,"breadcrumbs":4,"title":3},"191":{"body":47,"breadcrumbs":5,"title":4},"192":{"body":14,"breadcrumbs":3,"title":2},"193":{"body":20,"breadcrumbs":3,"title":2},"194":{"body":20,"breadcrumbs":4,"title":3},"195":{"body":23,"breadcrumbs":3,"title":2},"196":{"body":18,"breadcrumbs":4,"title":3},"197":{"body":13,"breadcrumbs":3,"title":2},"198":{"body":47,"breadcrumbs":4,"title":3},"199":{"body":11,"breadcrumbs":2,"title":1},"2":{"body":99,"breadcrumbs":5,"title":1},"20":{"body":5,"breadcrumbs":6,"title":2},"200":{"body":0,"breadcrumbs":5,"title":4},"201":{"body":24,"breadcrumbs":3,"title":2},"202":{"body":50,"breadcrumbs":3,"title":2},"203":{"body":19,"breadcrumbs":2,"title":1},"204":{"body":35,"breadcrumbs":5,"title":4},"205":{"body":181,"breadcrumbs":3,"title":2},"206":{"body":33,"breadcrumbs":2,"title":1},"207":{"body":133,"breadcrumbs":3,"title":2},"208":{"body":70,"breadcrumbs":2,"title":1},"209":{"body":33,"breadcrumbs":2,"title":1},"21":{"body":25,"breadcrumbs":5,"title":1},"210":{"body":49,"breadcrumbs":2,"title":1},"211":{"body":26,"breadcrumbs":2,"title":1},"212":{"body":0,"breadcrumbs":5,"title":4},"213":{"body":7,"breadcrumbs":3,"title":2},"214":{"body":12,"breadcrumbs":2,"title":1},"215":{"body":11,"breadcrumbs":3,"title":2},"216":{"body":9,"breadcrumbs":2,"title":1},"217":{"body":56,"breadcrumbs":2,"title":1},"218":{"body":64,"breadcrumbs":2,"title":1},"219":{"body":0,"breadcrumbs":5,"title":4},"22":{"body":87,"breadcrumbs":6,"title":2},"220":{"body":10,"breadcrumbs":3,"title":2},"221":{"body":61,"breadcrumbs":3,"title":2},"222":{"body":5,"breadcrumbs":2,"title":1},"223":{"body":66,"breadcrumbs":2,"title":1},"224":{"body":44,"breadcrumbs":2,"title":1},"225":{"body":12,"breadcrumbs":2,"title":1},"226":{"body":0,"breadcrumbs":5,"title":4},"227":{"body":14,"breadcrumbs":3,"title":2},"228":{"body":19,"breadcrumbs":3,"title":2},"229":{"body":34,"breadcrumbs":2,"title":1},"23":{"body":25,"breadcrumbs":6,"title":2},"230":{"body":34,"breadcrumbs":2,"title":1},"231":{"body":0,"breadcrumbs":5,"title":4},"232":{"body":92,"breadcrumbs":3,"title":2},"233":{"body":43,"breadcrumbs":2,"title":1},"234":{"body":10,"breadcrumbs":5,"title":4},"235":{"body":105,"breadcrumbs":3,"title":2},"236":{"body":51,"breadcrumbs":2,"title":1},"237":{"body":4,"breadcrumbs":2,"title":1},"238":{"body":3,"breadcrumbs":2,"title":1},"239":{"body":5,"breadcrumbs":5,"title":4},"24":{"body":47,"breadcrumbs":6,"title":2},"240":{"body":0,"breadcrumbs":5,"title":4},"241":{"body":149,"breadcrumbs":3,"title":2},"242":{"body":120,"breadcrumbs":2,"title":1},"243":{"body":65,"breadcrumbs":2,"title":1},"244":{"body":4,"breadcrumbs":2,"title":1},"245":{"body":19,"breadcrumbs":8,"title":7},"246":{"body":0,"breadcrumbs":5,"title":4},"247":{"body":9,"breadcrumbs":2,"title":1},"248":{"body":29,"breadcrumbs":2,"title":1},"249":{"body":6,"breadcrumbs":2,"title":1},"25":{"body":34,"breadcrumbs":6,"title":2},"250":{"body":0,"breadcrumbs":5,"title":4},"251":{"body":45,"breadcrumbs":2,"title":1},"252":{"body":77,"breadcrumbs":3,"title":2},"253":{"body":41,"breadcrumbs":2,"title":1},"254":{"body":34,"breadcrumbs":2,"title":1},"255":{"body":100,"breadcrumbs":4,"title":3},"256":{"body":0,"breadcrumbs":5,"title":4},"257":{"body":88,"breadcrumbs":2,"title":1},"258":{"body":14,"breadcrumbs":2,"title":1},"259":{"body":192,"breadcrumbs":3,"title":2},"26":{"body":39,"breadcrumbs":6,"title":2},"260":{"body":17,"breadcrumbs":2,"title":1},"261":{"body":209,"breadcrumbs":2,"title":1},"262":{"body":33,"breadcrumbs":5,"title":4},"263":{"body":0,"breadcrumbs":5,"title":4},"264":{"body":16,"breadcrumbs":3,"title":2},"265":{"body":0,"breadcrumbs":5,"title":4},"266":{"body":40,"breadcrumbs":2,"title":1},"267":{"body":78,"breadcrumbs":3,"title":2},"268":{"body":10,"breadcrumbs":2,"title":1},"269":{"body":0,"breadcrumbs":5,"title":4},"27":{"body":77,"breadcrumbs":5,"title":1},"270":{"body":34,"breadcrumbs":2,"title":1},"271":{"body":0,"breadcrumbs":5,"title":4},"272":{"body":7,"breadcrumbs":2,"title":1},"273":{"body":80,"breadcrumbs":2,"title":1},"274":{"body":0,"breadcrumbs":5,"title":4},"275":{"body":28,"breadcrumbs":2,"title":1},"276":{"body":26,"breadcrumbs":2,"title":1},"277":{"body":0,"breadcrumbs":5,"title":4},"278":{"body":31,"breadcrumbs":2,"title":1},"279":{"body":15,"breadcrumbs":2,"title":1},"28":{"body":0,"breadcrumbs":5,"title":1},"280":{"body":0,"breadcrumbs":5,"title":4},"281":{"body":43,"breadcrumbs":2,"title":1},"282":{"body":16,"breadcrumbs":3,"title":2},"283":{"body":32,"breadcrumbs":2,"title":1},"284":{"body":0,"breadcrumbs":5,"title":4},"285":{"body":77,"breadcrumbs":2,"title":1},"286":{"body":0,"breadcrumbs":5,"title":4},"287":{"body":36,"breadcrumbs":2,"title":1},"288":{"body":107,"breadcrumbs":2,"title":1},"289":{"body":0,"breadcrumbs":5,"title":4},"29":{"body":107,"breadcrumbs":7,"title":3},"290":{"body":7,"breadcrumbs":2,"title":1},"291":{"body":0,"breadcrumbs":5,"title":4},"292":{"body":16,"breadcrumbs":2,"title":1},"293":{"body":11,"breadcrumbs":2,"title":1},"294":{"body":0,"breadcrumbs":5,"title":4},"295":{"body":7,"breadcrumbs":2,"title":1},"296":{"body":13,"breadcrumbs":2,"title":1},"297":{"body":37,"breadcrumbs":2,"title":1},"298":{"body":0,"breadcrumbs":5,"title":4},"299":{"body":15,"breadcrumbs":2,"title":1},"3":{"body":0,"breadcrumbs":9,"title":5},"30":{"body":6,"breadcrumbs":6,"title":2},"300":{"body":33,"breadcrumbs":2,"title":1},"301":{"body":0,"breadcrumbs":5,"title":4},"302":{"body":8,"breadcrumbs":2,"title":1},"303":{"body":14,"breadcrumbs":2,"title":1},"304":{"body":22,"breadcrumbs":2,"title":1},"305":{"body":3,"breadcrumbs":5,"title":4},"31":{"body":17,"breadcrumbs":7,"title":2},"32":{"body":39,"breadcrumbs":7,"title":2},"33":{"body":77,"breadcrumbs":6,"title":1},"34":{"body":124,"breadcrumbs":7,"title":2},"35":{"body":23,"breadcrumbs":7,"title":2},"36":{"body":35,"breadcrumbs":7,"title":2},"37":{"body":31,"breadcrumbs":8,"title":3},"38":{"body":56,"breadcrumbs":9,"title":4},"39":{"body":39,"breadcrumbs":7,"title":2},"4":{"body":10,"breadcrumbs":6,"title":2},"40":{"body":34,"breadcrumbs":7,"title":2},"41":{"body":26,"breadcrumbs":7,"title":2},"42":{"body":8,"breadcrumbs":7,"title":2},"43":{"body":5,"breadcrumbs":11,"title":4},"44":{"body":59,"breadcrumbs":8,"title":1},"45":{"body":42,"breadcrumbs":9,"title":2},"46":{"body":44,"breadcrumbs":10,"title":3},"47":{"body":23,"breadcrumbs":9,"title":2},"48":{"body":32,"breadcrumbs":9,"title":2},"49":{"body":76,"breadcrumbs":8,"title":1},"5":{"body":54,"breadcrumbs":5,"title":1},"50":{"body":62,"breadcrumbs":11,"title":4},"51":{"body":7,"breadcrumbs":9,"title":2},"52":{"body":33,"breadcrumbs":8,"title":2},"53":{"body":76,"breadcrumbs":8,"title":2},"54":{"body":110,"breadcrumbs":8,"title":2},"55":{"body":63,"breadcrumbs":8,"title":2},"56":{"body":60,"breadcrumbs":8,"title":2},"57":{"body":43,"breadcrumbs":9,"title":3},"58":{"body":91,"breadcrumbs":8,"title":2},"59":{"body":44,"breadcrumbs":7,"title":1},"6":{"body":26,"breadcrumbs":6,"title":2},"60":{"body":79,"breadcrumbs":10,"title":4},"61":{"body":195,"breadcrumbs":11,"title":5},"62":{"body":57,"breadcrumbs":8,"title":2},"63":{"body":15,"breadcrumbs":7,"title":1},"64":{"body":5,"breadcrumbs":8,"title":2},"65":{"body":57,"breadcrumbs":6,"title":2},"66":{"body":0,"breadcrumbs":6,"title":2},"67":{"body":20,"breadcrumbs":5,"title":1},"68":{"body":41,"breadcrumbs":6,"title":2},"69":{"body":38,"breadcrumbs":6,"title":2},"7":{"body":7,"breadcrumbs":4,"title":1},"70":{"body":48,"breadcrumbs":6,"title":2},"71":{"body":86,"breadcrumbs":6,"title":2},"72":{"body":120,"breadcrumbs":6,"title":2},"73":{"body":25,"breadcrumbs":6,"title":2},"74":{"body":154,"breadcrumbs":9,"title":5},"75":{"body":0,"breadcrumbs":6,"title":2},"76":{"body":39,"breadcrumbs":6,"title":2},"77":{"body":17,"breadcrumbs":6,"title":2},"78":{"body":70,"breadcrumbs":7,"title":3},"79":{"body":13,"breadcrumbs":6,"title":2},"8":{"body":15,"breadcrumbs":5,"title":2},"80":{"body":73,"breadcrumbs":5,"title":1},"81":{"body":99,"breadcrumbs":7,"title":3},"82":{"body":60,"breadcrumbs":7,"title":3},"83":{"body":53,"breadcrumbs":7,"title":3},"84":{"body":72,"breadcrumbs":9,"title":5},"85":{"body":148,"breadcrumbs":6,"title":2},"86":{"body":178,"breadcrumbs":9,"title":5},"87":{"body":7,"breadcrumbs":6,"title":2},"88":{"body":14,"breadcrumbs":5,"title":1},"89":{"body":45,"breadcrumbs":6,"title":2},"9":{"body":66,"breadcrumbs":7,"title":4},"90":{"body":25,"breadcrumbs":5,"title":1},"91":{"body":18,"breadcrumbs":6,"title":2},"92":{"body":38,"breadcrumbs":5,"title":1},"93":{"body":28,"breadcrumbs":5,"title":1},"94":{"body":15,"breadcrumbs":5,"title":1},"95":{"body":23,"breadcrumbs":5,"title":1},"96":{"body":0,"breadcrumbs":6,"title":2},"97":{"body":135,"breadcrumbs":7,"title":3},"98":{"body":7,"breadcrumbs":6,"title":2},"99":{"body":27,"breadcrumbs":9,"title":3}},"docs":{"0":{"body":"","breadcrumbs":"Get excited » Get excited","id":"0","title":"Get excited"},"1":{"body":"A reactive database framework Build powerful React and React Native apps that scale from hundreds to tens of thousands of records and remain fast ⚡️ WatermelonDB ⚡️ Launch your app instantly no matter how much data you have 📈 Highly scalable from hundreds to tens of thousands of records 😎 Lazy loaded . Only load data when you need it 🔄 Offline-first. Sync with your own backend 📱 Multiplatform . iOS, Android, web, and Node.js ⚛️ Optimized for React. Easily plug data into components 🧰 Framework-agnostic. Use JS API to plug into other UI frameworks ⏱ Fast. And getting faster with every release! ✅ Proven. Powers Nozbe Teams since 2017 (and many others ) ✨ Reactive. (Optional) RxJS API 🔗 Relational. Built on rock-solid SQLite foundation ⚠️ Static typing with Flow or TypeScript WatermelonDB is a new way of dealing with user data in React Native and React web apps. It's optimized for building complex applications in React Native, and the number one goal is real-world performance . In simple words, your app must launch fast . For simple apps, using Redux or MobX with a persistence adapter is the easiest way to go. But when you start scaling to thousands or tens of thousands of database records, your app will now be slow to launch (especially on slower Android devices). Loading a full database into JavaScript is expensive! Watermelon fixes it by being lazy . Nothing is loaded until it's requested. And since all querying is performed directly on the rock-solid SQLite database on a separate native thread, most queries resolve in an instant. But unlike using SQLite directly, Watermelon is fully observable . So whenever you change a record, all UI that depends on it will automatically re-render. For example, completing a task in a to-do app will re-render the task component, the list (to reorder), and all relevant task counters. Learn more . 📺 Next-generation React databases(a talk about WatermelonDB) ✨ Check out web Demo","breadcrumbs":"Get excited » Check out the README » Why Watermelon?","id":"1","title":"Why Watermelon?"},"10":{"body":"To compile the WatermelonDB demo on your own machine: Download this project git clone https://github.com/Nozbe/WatermelonDB.git\ncd WatermelonDB/examples/web\nyarn Run the server: yarn dev Webpack will point you to the right URL to open in the browser You can also use Now to deploy the demo app (requires a Zeit account): now ⚠️ You might want to git checkout the latest stable tag if the demo app doesn't work","breadcrumbs":"Get excited » See the demo » Running web demo","id":"10","title":"Running web demo"},"100":{"body":"Here is an inline writer, you can invoke it anywhere you have access to the database object: // Note: function passed to `database.write()` MUST be asynchronous\nconst newPost = await database.write(async => { const post = await database.get('posts').create(post => { post.title = 'New post' post.body = 'Lorem ipsum...' }) const comment = await database.get('comments').create(comment => { comment.post.set(post) comment.author.id = someUserId comment.body = 'Great post!' }) // Note: Value returned from the wrapped function will be returned to `database.write` caller return post\n})","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Inline writers","id":"100","title":"Inline writers"},"101":{"body":"Writer methods can be defined on Model subclasses by using the @writer decorator: import { writer } from '@rikishi/watermelondb/decorators' class Post extends Model { // ... @writer async addComment(body, author) { const newComment = await this.collections.get('comments').create(comment => { comment.post.set(this) comment.author.set(author) comment.body = body }) return newComment }\n} We highly recommend defining writer methods on Models to organize all code that changes the database in one place, and only use inline writers sporadically. Note that this is the same as defining a simple method that wraps all work in database.write() - using @writer is simply more convenient. Note: Always mark actions as async and remember to await on .create() and .update() You can use this.collections to access Database.collections Another example : updater action on Comment: class Comment extends Model { // ... @field('is_spam') isSpam @writer async markAsSpam() { await this.update(comment => { comment.isSpam = true }) }\n} Now we can create a comment and immediately mark it as spam: const comment = await post.addComment('Lorem ipsum', someUser)\nawait comment.markAsSpam()","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Writer methods","id":"101","title":"Writer methods"},"102":{"body":"When you make multiple changes in a writer, it's best to batch them . Batching means that the app doesn't have to go back and forth with the database (sending one command, waiting for the response, then sending another), but instead sends multiple commands in one big batch. This is faster, safer, and can avoid subtle bugs in your app Take an action that changes a Post into spam: class Post extends Model { // ... @writer async createSpam() { await this.update(post => { post.title = `7 ways to lose weight` }) await this.collections.get('comments').create(comment => { comment.post.set(this) comment.body = \"Don't forget to comment, like, and subscribe!\" }) }\n} Let's modify it to use batching: class Post extends Model { // ... @writer async createSpam() { await this.batch( this.prepareUpdate(post => { post.title = `7 ways to lose weight` }), this.collections.get('comments').prepareCreate(comment => { comment.post.set(this) comment.body = \"Don't forget to comment, like, and subscribe!\" }) ) }\n} Note : You can call await this.batch within @writer methods only. You can also call database.batch() within a database.write() block. Pass the list of prepared operations as arguments: Instead of calling await record.update(), pass record.prepareUpdate() — note lack of await Instead of await collection.create(), use collection.prepareCreate() Instead of await record.markAsDeleted(), use record.prepareMarkAsDeleted() Instead of await record.destroyPermanently(), use record.prepareDestroyPermanently() Advanced: you can pass collection.prepareCreateFromDirtyRaw({ put your JSON here }) You can pass falsy values (null, undefined, false) to batch — they will simply be ignored. You can also pass a single array argument instead of a list of arguments","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Batch updates","id":"102","title":"Batch updates"},"103":{"body":"When you delete, say, a Post, you generally want all Comments that belong to it to be deleted as well. To do this, override markAsDeleted() (or destroyPermanently() if you don't sync) to explicitly delete all children as well. class Post extends Model { static table = 'posts' static associations = { comments: { type: 'has_many', foreignKey: 'post_id' }, } @children('comments') comments async markAsDeleted() { await this.comments.destroyAllPermanently() await super.markAsDeleted() }\n} Then to actually delete the post: database.write(async () => { await post.markAsDeleted()\n}) Note: Use Query.destroyAllPermanently() on all dependent @children you want to delete Remember to call super.markAsDeleted — at the end of the method!","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Delete action","id":"103","title":"Delete action"},"104":{"body":"WatermelonDB is highly asynchronous, which is a BIG challange in terms of achieving consistent data. Read this only if you are curious: Why are readers and writers necessary? Consider a function markCommentsAsSpam that fetches a list of comments on a post, and then marks them all as spam. The two operations (fetching, and then updating) are asynchronous, and some other operation that modifies the database could run in between. And it could just happen to be a function that adds a new comment on this post. Even though the function completes successfully , it wasn't actually successful at its job. This example is trivial. But others may be far more dangerous. If a function fetches a record to perform an update on, this very record could be deleted midway through, making the action fail (and potentially causing the app to crash, if not handled properly). Or a function could have invariants determining whether the user is allowed to perform an action, that would be invalidated during action's execution. Or, in a collaborative app where access permissions are represented by another object, parallel execution of different actions could cause those access relations to be left in an inconsistent state. The worst part is that analyzing all possible interactions for dangers is very hard, and having sync that runs automatically makes them very likely. Solution? Group together related reads and writes together in an Writer, enforce that all writes MUST occur in a Writer, and only allow one Writer to run at the time. This way, it's guaranteed that in a Writer, you're looking at a consistent view of the world. Most simple reads are safe to do without groupping them, however if you have multiple related reads, you also need to wrap them in a Reader.","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Advanced: Why are readers and writers necessary?","id":"104","title":"Advanced: Why are readers and writers necessary?"},"105":{"body":"Readers are an advanced feature you'll rarely need. Because WatermelonDB is asynchronous, if you make multiple separate queries, normally you have no guarantee that no records were created, updated, or deleted between fetching these queries. Code within a Reader, however, has a guarantee that for the duration of the Reader, no changes will be made to the database (more precisely, no Writer can execute during Reader's work). For example, if you were writing a custom XML data export feature for your app, you'd want the information there to be fully consistent. Therefore, you'd wrap all queries within a Reader: database.read(async () => { // no changes will happen to the database until this function exits\n}) // alternatively:\nclass Blog extends Model { // ... @reader async exportBlog() { const posts = await this.posts.fetch() const comments = await this.allComments.fetch() // ... }\n}","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Advanced: Readers","id":"105","title":"Advanced: Readers"},"106":{"body":"If you try to call a Writer from another Writer, you'll notice that it won't work. This is because while a Writer is running, no other Writer can run simultaneously. To override this behavior, wrap the Writer call in this.callWriter: class Comment extends Model { // ... @writer async appendToPost() { const post = await this.post.fetch() // `appendToBody` is an `@writer` on `Post`, so we call callWriter to allow it await this.callWriter(() => post.appendToBody(this.body)) }\n} // alternatively:\ndatabase.write(async writer => { const post = await database.get('posts').find('abcdef') await writer.callWriter(() => post.appendToBody('Lorem ipsum...')) // appendToBody is a @writer\n}) The same is true with Readers - use callReader to nest readers.","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Advanced: nesting writers or readers","id":"106","title":"Advanced: nesting writers or readers"},"107":{"body":"➡️ Now that you've mastered all basics of Watermelon, go create some powerful apps — or keep reading advanced guides","breadcrumbs":"Learn to use Watermelon » Writers, Readers, batching » Next steps","id":"107","title":"Next steps"},"108":{"body":"Advanced guides for using WatermelonDB","breadcrumbs":"Advanced guides » Advanced guides","id":"108","title":"Advanced guides"},"109":{"body":"Schema migrations is the mechanism by which you can add new tables and columns to the database in a backward-compatible way. Without migrations, if a user of your app upgrades from one version to another, their local database will be cleared at launch, and they will lose all their data. ⚠️ Always use migrations!","breadcrumbs":"Advanced guides » Migrations » Migrations","id":"109","title":"Migrations"},"11":{"body":"Learn the basics of how to use WatermelonDB","breadcrumbs":"Learn to use Watermelon » Learn to use Watermelon","id":"11","title":"Learn to use Watermelon"},"110":{"body":"Add a new file for migrations: // app/model/migrations.js import { schemaMigrations } from '@rikishi/watermelondb/Schema/migrations' export default schemaMigrations({ migrations: [ // We'll add migration definitions here later ],\n}) Hook up migrations to the Database adapter setup: // index.js\nimport migrations from 'model/migrations' const adapter = new SQLiteAdapter({ schema: mySchema, migrations,\n})","breadcrumbs":"Advanced guides » Migrations » Migrations setup","id":"110","title":"Migrations setup"},"111":{"body":"When you make schema changes when you use migrations, be sure to do this in this specific order, to minimize the likelihood of making an error.","breadcrumbs":"Advanced guides » Migrations » Migrations workflow","id":"111","title":"Migrations workflow"},"112":{"body":"First, define the migration - that is, define the change that occurs between two versions of schema (such as adding a new table, or a new table column). Don't change the schema file yet! // app/model/migrations.js import { schemaMigrations, createTable } from '@rikishi/watermelondb/Schema/migrations' export default schemaMigrations({ migrations: [ { // ⚠️ Set this to a number one larger than the current schema version toVersion: 2, steps: [ // See \"Migrations API\" for more details createTable({ name: 'comments', columns: [ { name: 'post_id', type: 'string', isIndexed: true }, { name: 'body', type: 'string' }, ], }), ], }, ],\n}) Refresh your simulator/browser. You should see this error: Migrations can't be newer than schema. Schema is version 1 and migrations cover range from 1 to 2 If so, good, move to the next step! But you might also see an error like \"Missing table name in schema\", which means you made an error in defining migrations. See \"Migrations API\" below for details.","breadcrumbs":"Advanced guides » Migrations » Step 1: Add a new migration","id":"112","title":"Step 1: Add a new migration"},"113":{"body":"Now it's time to make the actual changes to the schema file — add the same tables or columns as in your migration definition ⚠️ Please double and triple check that your changes to schema match exactly the change you defined in the migration. Otherwise you risk that the app will work when the user migrates, but will fail if it's a fresh install — or vice versa. ⚠️ Don't change the schema version yet // model/schema.js export default appSchema({ version: 1, tables: [ // This is our new table! tableSchema({ name: 'comments', columns: [ { name: 'post_id', type: 'string', isIndexed: true }, { name: 'body', type: 'string' }, ], }), // ... ]\n}) Refresh the simulator. You should again see the same \"Migrations can't be newer than schema\" error. If you see a different error, you made a syntax error.","breadcrumbs":"Advanced guides » Migrations » Step 2: Make matching changes in schema","id":"113","title":"Step 2: Make matching changes in schema"},"114":{"body":"Now that we made matching changes in the schema (source of truth about tables and columns) and migrations (the change in tables and columns), it's time to commit the change by bumping the version: // model/schema.js export default appSchema({ version: 2, tables: [ // ... ]\n}) If you refresh again, your app should show up without issues — but now you can use the new tables/columns","breadcrumbs":"Advanced guides » Migrations » Step 3: Bump schema version","id":"114","title":"Step 3: Bump schema version"},"115":{"body":"Before shipping a new version of the app, please check that your database changes are all compatible: Migrations test: Install the previous version of your app, then update to the version you're about to ship, and make sure it still works Fresh schema install test: Remove the app, and then install the new version of the app, and make sure it works","breadcrumbs":"Advanced guides » Migrations » Step 4: Test your migrations","id":"115","title":"Step 4: Test your migrations"},"116":{"body":"It's simply because React Native simulator (and often React web projects) are configured to automatically refresh when you save a file. You don't want to database to accidentally migrate (upgrade) with changes that have a mistake, or changes you haven't yet completed making. By making migrations first, and bumping version last, you can double check you haven't made a mistake.","breadcrumbs":"Advanced guides » Migrations » Why is this order important","id":"116","title":"Why is this order important"},"117":{"body":"Each migration must migrate to a version one above the previous migration, and have multiple steps (such as adding a new table, or new columns). Larger example: schemaMigrations({ migrations: [ { toVersion: 3, steps: [ createTable({ name: 'comments', columns: [ { name: 'post_id', type: 'string', isIndexed: true }, { name: 'body', type: 'string' }, ], }), addColumns({ table: 'posts', columns: [ { name: 'subtitle', type: 'string', isOptional: true }, { name: 'is_pinned', type: 'boolean' }, ], }), ], }, { toVersion: 2, steps: [ // ... ], }, ],\n})","breadcrumbs":"Advanced guides » Migrations » Migrations API","id":"117","title":"Migrations API"},"118":{"body":"createTable({ name: 'table_name', columns: [ ... ] }) - same API as tableSchema() addColumns({ table: 'table_name', columns: [ ... ] }) - you can add one or multiple columns to an existing table. The columns table has the same format as in schema definitions Other types of migrations (e.g. deleting or renaming tables and columns) are not yet implemented. See migrations/index.js . Please contribute!","breadcrumbs":"Advanced guides » Migrations » Migration steps:","id":"118","title":"Migration steps:"},"119":{"body":"When you're not using migrations, the database will reset (delete all its contents) whenever you change the schema version. If the migration fails, the database will fail to initialize, and will roll back to previous version. This is unlikely, but could happen if you, for example, create a migration that tries to create the same table twice. The reason why the database will fail instead of reset is to avoid losing user data (also it's less confusing in development). You can notice the problem, fix the migration, and ship it again without data loss. When database in the running app has newer database version than the schema version defined in code, the database will reset (clear its contents). This is useful in development If there's no available migrations path (e.g. user has app with database version 4, but oldest migration is from version 10 to 11), the database will reset.","breadcrumbs":"Advanced guides » Migrations » Database reseting and other edge cases","id":"119","title":"Database reseting and other edge cases"},"12":{"body":"First, add Watermelon to your project: yarn add @rikishi/watermelondb # (or with npm:)\nnpm install @rikishi/watermelondb","breadcrumbs":"Learn to use Watermelon » Installation » Installation","id":"12","title":"Installation"},"120":{"body":"There's no automatic \"rollback\" feature in Watermelon. If you make a mistake in migrations during development, roll back in this order: Comment out any changes made to schema.js Comment out any changes made to migrations.js Decrement schema version number (bring back the original number) After refreshing app, the database should reset to previous state. Now you can correct your mistake and apply changes again (please do it in order described in \"Migrations workflow\").","breadcrumbs":"Advanced guides » Migrations » Rolling back changes","id":"120","title":"Rolling back changes"},"121":{"body":"Similar to Schema , you can add unsafeSql parameter to every migration step to modify or replace SQL generated by WatermelonDB to perform the migration. There is also an unsafeExecuteSql('some sql;') step you can use to append extra SQL. Those are ignored with LokiJSAdapter and for the purposes of migration syncs .","breadcrumbs":"Advanced guides » Migrations » Unsafe SQL migrations","id":"121","title":"Unsafe SQL migrations"},"122":{"body":"WatermelonDB has been designed from scratch to be able to seamlessly synchronize with a remote database (and, therefore, keep multiple copies of data synced with each other). Note that Watermelon is only a local database — you need to bring your own backend . What Watermelon provides are: Synchronization primitives — information about which records were created, updated, or deleted locally since the last sync — and which columns exactly were modified. You can build your own custom sync engine using those primitives Built-in sync adapter — You can use the sync engine Watermelon provides out of the box, and you only need to provide two API endpoints on your backend that conform to Watermelon sync protocol","breadcrumbs":"Advanced guides » Sync » Synchronization","id":"122","title":"Synchronization"},"123":{"body":"To synchronize, you need to pass pullChanges and pushChanges (optional) that talk to your backend and are compatible with Watermelon Sync Protocol. The frontend code will look something like this: import { synchronize } from '@rikishi/watermelondb/sync' async function mySync() { await synchronize({ database, pullChanges: async ({ lastPulledAt, schemaVersion, migration }) => { const urlParams = `last_pulled_at=${lastPulledAt}&schema_version=${schemaVersion}&migration=${encodeURIComponent(JSON.stringify(migration))}` const response = await fetch(`https://my.backend/sync?${urlParams}`) if (!response.ok) { throw new Error(await response.text()) } const { changes, timestamp } = await response.json() return { changes, timestamp } }, pushChanges: async ({ changes, lastPulledAt }) => { const response = await fetch(`https://my.backend/sync?last_pulled_at=${lastPulledAt}`, { method: 'POST', body: JSON.stringify(changes) }) if (!response.ok) { throw new Error(await response.text()) } }, migrationsEnabledAtVersion: 1, })\n} Who calls synchronize()? Upon looking at the example above, one question that may arise is who will call synchronize() -- or, in the example above mySync(). WatermelonDB does not manage the moment of invocation of the synchronize() function in any way. The database assumes every call of pullChanges will return all the changes that haven't yet been replicated (up to last_pulled_at). The application code is responsible for calling synchronize() in the frequence it deems necessary.","breadcrumbs":"Advanced guides » Sync » Using synchronize() in your app","id":"123","title":"Using synchronize() in your app"},"124":{"body":"⚠️ Note about a React Native / UglifyES bug . When you import Watermelon Sync, your app might fail to compile in release mode. To fix this, configure Metro bundler to use Terser instead of UglifyES. Run: yarn add metro-minify-terser Then, update metro.config.js: module.exports = { // ... transformer: { // ... minifierPath: 'metro-minify-terser', },\n} You might also need to switch to Terser in Webpack if you use Watermelon for web.","breadcrumbs":"Advanced guides » Sync » Troubleshooting","id":"124","title":"Troubleshooting"},"125":{"body":"Watermelon will call this function to ask for changes that happened on the server since the last pull. Arguments: lastPulledAt is a timestamp for the last time client pulled changes from server (or null if first sync) schemaVersion is the current schema version of the local database migration is an object representing schema changes since last sync (or null if up to date or not supported) This function should fetch from the server the list of ALL changes in all collections since lastPulledAt. You MUST pass an async function or return a Promise that eventually resolves or rejects You MUST pass lastPulledAt, schemaVersion, and migration to an endpoint that conforms to Watermelon Sync Protocol You MUST return a promise resolving to an object of this shape (your backend SHOULD return this shape already): { changes: { ... }, // valid changes object timestamp: 100000, // integer with *server's* current time\n} You MUST NOT store the object returned in pullChanges(). If you need to do any processing on it, do it before returning the object. Watermelon treats this object as \"consumable\" and can mutate it (for performance reasons)","breadcrumbs":"Advanced guides » Sync » Implementing pullChanges()","id":"125","title":"Implementing pullChanges()"},"126":{"body":"Watermelon will call this function with a list of changes that happened locally since the last push so you can post it to your backend. Arguments passed: { changes: { ... }, // valid changes object lastPulledAt: 10000, // the timestamp of the last successful pull (timestamp returned in pullChanges)\n} You MUST pass changes and lastPulledAt to a push sync endpoint conforming to Watermelon Sync Protocol You MUST pass an async function or return a Promise from pushChanges() pushChanges() MUST resolve after and only after the backend confirms it successfully received local changes pushChanges() MUST reject if backend failed to apply local changes You MUST NOT resolve sync prematurely or in case of backend failure You MUST NOT mutate or store arguments passed to pushChanges(). If you need to do any processing on it, do it before returning the object. Watermelon treats this ob