diff --git a/images/migration-guides/mongodb-after.png b/images/migration-guides/mongodb-after.png
new file mode 100644
index 0000000..d200b56
Binary files /dev/null and b/images/migration-guides/mongodb-after.png differ
diff --git a/images/migration-guides/mongodb-before.png b/images/migration-guides/mongodb-before.png
new file mode 100644
index 0000000..eb1509a
Binary files /dev/null and b/images/migration-guides/mongodb-before.png differ
diff --git a/migration-guides/mongodb-atlas.mdx b/migration-guides/mongodb-atlas.mdx
index a64fedc..ab63b1b 100644
--- a/migration-guides/mongodb-atlas.mdx
+++ b/migration-guides/mongodb-atlas.mdx
@@ -1,102 +1,477 @@
---
title: "MongoDB Atlas Device Sync Migration Guide"
+description: "This guide lays out all the steps of migrating from MongoDB Atlas Device Sync to PowerSync."
---
-We are currently working on a more detailed migration guide. In the meantime, the below guide provides high-level migration information.
+## Introduction
-## MongoDB Atlas deprecation
+Moving to PowerSync allows you to benefit from efficient data synchronization using open and proven technologies. Users get always-available, instantly-responsive offline-first apps that also stream data updates in real-time when online.
-In September 2024, MongoDB [deprecated](https://www.mongodb.com/docs/atlas/app-services/deprecation/#std-label-app-services-deprecation) the following services:
+## Why PowerSync?
-* Atlas Device Sync
-* Atlas Data API
-* Atlas Device SDKs
+PowerSync’s history goes as far back as 2009, when the original version of the sync engine was developed as part of an app development platform used by some of the world’s largest industrial companies to provide employees with offline-capable business apps deployed in harsh environments ([learn more](https://www.powersync.com/company) about PowerSync’s history).
-If you have MongoDB Atlas Device Sync deployed today, at a high level your architecture probably looks something like this:
-
-
-
+PowerSync was spun off as a standalone product in 2023, and gives engineering teams a proven, open and robust sync engine with a familiar **server-client** [architecture](/architecture/architecture-overview#architecture-overview).
-## PowerSync beta support for MongoDB
+PowerSync’s MongoDB connector has been **developed in collaboration with MongoDB** to provide an easy setup process. It is currently considered [ready for production-use](https://www.powersync.com/blog/powersync-mongodb-connector-module-now-in-beta), provided that you have adequately tested your use cases. It is already being used in production by MongoDB customers.
-As of December 2024, support for MongoDB as a backend database source in PowerSync is available in [a Beta release](https://www.powersync.com/blog/powersync-mongodb-connector-module-now-in-beta). This means that MongoDB replication is considered ready for production-use, provided that you have adequately tested your use cases.
+The server-side [PowerSync Service](/architecture/powersync-service#powersync-service) connects to MongoDB and pre-processes and pre-indexes data to be efficiently synced to users based on defined “Sync Rules”. Client applications connect to the PowerSync Service to sync data relevant to each user. Incremental updates in MongoDB are synced to clients in real-time.
-It is currently available in [PowerSync Cloud](https://www.powersync.com/pricing) and self-hosted environments:
+Client applications can read and write data to the local client-side database. PowerSync provides for bi-directional syncing so that writes in the local client-side databases are automatically synced back to the source MongoDB database. If users are offline or have patchy connectivity, PowerSync automatically manages network failures and retries.
-* **PowerSync Cloud**: Refer to [Database Connection](/installation/database-connection#mongodb-beta-specifics)
-* **Self-hosted**: An end-to-end demo app using Docker Compose is [available here](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs-mongodb). Please refer to its README for further instructions.
+By introducing PowerSync as a sync engine, you get:
+* **Predictable sync behavior** that syncs relevant data to each user.
+* **Consistency guarantees** ensuring consistent state of the client-side database.
+* **Real-time multi-user applications** as data updates are streamed to connected clients in real-time.
+* **Instantly responsive user experience** as the user interaction with the app is unaffected by the network.
+* **Offline-first capabilities** enabling apps to continue to work regardless of network conditions.
-Migrating to PowerSync would take you to this: (new components in green)
+Please review this guide to understand the required changes and prerequisites. Following the provided steps will help your team transition smoothly.
-
-
-
+If you need further assistance at any point, you can:
+* [Set up a call](https://calendly.com/powersync/powersync-chat) with PowerSync engineers.
+* Ask us anything on our [Discord server](https://discord.gg/powersync).
+* [Contact us](mailto:hello@powersync.com) through email.
-### Explanation of the migrated architecture
+## Architecture: Before and After
+If you have MongoDB Atlas Device Sync deployed today, at a high level your architecture will look something like this:
-1. PowerSync handles writes differently from reads.
- * **Reads:** The PowerSync Service connects to your MongoDB database and replicates data in real-time to PowerSync clients. Reads are partitioned using PowerSync's [Sync Rules](/usage/sync-rules)
- * Sync Rules are more flexible than MongoDB Realm Flexible Sync, but are defined server-side and not client-side.
- * **Writes:** The client-side application can perform writes directly on the local SQLite database. The writes are also automatically placed into an upload queue by the PowerSync client SDK. The SDK then uses a developer-defined `uploadData()` function to manage the uploading of those writes sequentially to the backend. In the `uploadData()` function, the developer calls their backend API.
- * The backend can be a very thin API layer or can contain significant amounts of logic — [it is up to the developer](/installation/app-backend-setup/writing-client-changes)
-2. The PowerSync Service is generally [available](https://powersync.com/pricing) as a cloud-hosted service (PowerSync Cloud), or you can self-host using our Open Edition.
-3. The PowerSync Client SDKs use **SQLite** under the hood. Even though MongoDB is a "NoSQL" document database, PowerSync's use of SQLite works well with MongoDB, since the [PowerSync protocol](/architecture/powersync-protocol) is schemaless (it syncs schemaless JSON data) and we dynamically apply a [client-side schema](/installation/client-side-setup/define-your-schema) to the data in SQLite using SQLite views.
- * The client-side will most likely be where the bulk of migration work takes place for customers migrating from Atlas Device Sync to PowerSync. See [this post on our blog](https://www.powersync.com/blog/migrating-a-mongodb-atlas-device-sync-app-to-powersync) for a migration example.
- * Client-side queries are typically written in SQL. [ORM support](https://www.powersync.com/blog/using-orms-with-powersync) is available for our JavaScript ([React Native](/client-sdk-references/javascript-web/javascript-orm/overview) and [web](/client-sdk-references/javascript-web/javascript-orm/overview)) and Flutter/[Dart](/client-sdk-references/flutter/flutter-orm-support) Client SDKs.
-4. PowerSync piggybacks off your existing authentication, however, it uses JWTs. If you were using Atlas Device SDKs for authentication, you will need to implement an authentication provider.
+
-### Migration steps
+Migrating to PowerSync results in this architecture: (new components in green)
-The blog post [Migrating a MongoDB Atlas Device Sync App to PowerSync](https://www.powersync.com/blog/migrating-a-mongodb-atlas-device-sync-app-to-powersync) can serve as a handy guide through the migration process.
+
-1. Connect your database to an instance of the PowerSync Service:
- 1. **PowerSync Cloud**: See [Database Connection](/installation/database-connection#mongodb-alpha-specifics)
- 2. **Self-hosted**: Refer to our end-to-end demo app [available here](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs-mongodb)
-2. Define [Sync Rules](/usage/sync-rules) in PowerSync — this enables dynamic partial replication so that you don't need to sync your entire MongoDB database to devices.
- 1. We recommend starting with one or two simple [Global Data](/usage/sync-rules/example-global-data) queries
-3. Generate a [Development Token](/installation/authentication-setup/development-tokens)
-4. Use our [Diagnostics App](https://diagnostics-app.powersync.com/) to validate that MongoDB is syncing into SQLite as expected.
-5. Implement an app using one of our [Client SDKs](/client-sdk-references/introduction)
- 1. You can continue using your _Development Token_ for authentication as a start.
- 2. We recommend implementing a "Hello World" app to get a feel for how PowerSync works, _or_ you can jump straight into pulling the Client SDK into your existing app.
- 3. Verify that syncing from MongoDB is working.
-6. Implement [authentication](/installation/authentication-setup/custom)
-7. Implement your backend app to accept writes. There are currently two options:
- 1. **Custom self-hosted backend:** If you already have a backend application as part of your stack, you should use your existing backend. Alternatively, we have Node.js, Django and Rails example implementations available [here](/resources/demo-apps-example-projects#custom-backend-examples).
- 2. **CloudCode hosted/managed backend:** An alternative option is to use CloudCode, a serverless cloud functions environment provided by us. We provide a template that you can use as a turnkey starting point. See our [documentation here](/usage/tools/cloudcode).
+Here is a quick overview of the resulting PowerSync architecture:
+* **PowerSync Service** is available as a cloud-hosted service ([PowerSync Cloud](https://powersync.com/pricing)), or you can self-host using our Open Edition.
+* **Authentication**: PowerSync piggybacks off your app’s existing authentication, and JWTs are used to authenticate between clients and the PowerSync Service. If you are using Atlas Device SDKs for authentication, you will need to implement an authentication provider.
+* **PowerSync Client SDKs** use **SQLite** under the hood. Even though MongoDB is a “NoSQL” document database, PowerSync’s use of SQLite works well with MongoDB, since the [PowerSync protocol](/architecture/powersync-protocol) is schemaless (it syncs schemaless JSON data) and we dynamically apply a [client-side schema](/installation/client-side-setup/define-your-schema) to the data in SQLite using SQLite views. Client-side queries can be written in SQL or you can make use of an ORM (we provide a few [ORM integrations](https://www.powersync.com/blog/using-orms-with-powersync))
+* **Reads vs Writes**: PowerSync handles syncing of reads differently from writes.
+ * **Reads**: The PowerSync Service connects to your MongoDB database and replicates data in real-time to PowerSync clients. Reads are configured using PowerSync’s [“Sync Rules”](/usage/sync-rules/). Sync Rules are more flexible than MongoDB Realm Flexible Sync, but are defined on the server-side, not on the client-side.
+ * **Writes**: The client-side application can perform writes directly on the local SQLite database. The writes are also automatically placed into an upload queue by the PowerSync Client SDK. The SDK then uses a developer-defined `uploadData()` function to manage the uploading of those writes sequentially to the backend.
+* **Authorization**: Authorization is controlled separately for reads vs. writes.
+ * **Reads**: The [Sync Rules](/usage/sync-rules/) control which users can access which data.
+ * **Writes**: The backend controls authorization for how users can modify data.
+* **Backend**: PowerSync requires a backend API interface to upload writes to MongoDB. There are currently two options:
+ * **Custom self-hosted backend**: If you already have a backend application as part of your stack, you should use your existing backend. If you don’t yet have one: We have Node.js, Django and Rails [example implementations](/resources/demo-apps-example-projects#custom-backend-examples) available.
+ * **Serverless cloud functions (hosted/managed)**: An alternative option is to use CloudCode, a serverless cloud functions environment provided by PowerSync. We have a template available that you can use as a turnkey starting point.
-### Additional notes
-#### PowerSync Service
+## Migration Steps
-The PowerSync Service can either be self-hosted or deployed using PowerSync Cloud.
+Follow the steps below to migrate a MongoDB Atlas Device Sync app to PowerSync.
-To [self-host](https://github.com/powersync-ja/powersync-service?tab=readme-ov-file), you need a Docker environment. Refer to our end-to-end demo app [available here](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs-mongodb) which uses Docker Compose.
+### 1. Remove Realm
-#### PowerSync Client SDKs
+Before adding PowerSync, remove MongoDB Realm from your project. This includes uninstalling the Realm SDK and deleting all Realm-related code and dependencies. It is also possible to initially run Realm and PowerSync in parallel, and remove Realm later.
-We are constantly adding additional [PowerSync Client SDKs](/client-sdk-references/introduction) and some of them are under very active development. Below are the release status for each language/platform:
+### 2. Create PowerSync account and instance
-| Client SDK | Status | Release ETA |
-| ------------------------------------------------------------------- | ----------- | ---------------- |
-| [JavaScript/Web](/client-sdk-references/javascript-web) | Stable | N/A |
-| [React Native & Expo](/client-sdk-references/react-native-and-expo) | Stable | N/A |
-| [Flutter/Dart](/client-sdk-references/flutter) | Stable | N/A |
-| [Kotlin Multiplatform](/client-sdk-references/kotlin-multiplatform) | Beta | V1 ETA 2025Q1 |
-| [Swift](/client-sdk-references/swift) | Beta | V1 ETA 2025Q1 |
-| Node.js | PoC | Alpha ETA 2025Q1 |
-| .NET | In Progress | Alpha ETA 2025Q1 |
+To get started quickly with PowerSync, sign up for a free PowerSync Cloud account [here](https://accounts.journeyapps.com/portal/powersync-signup?s=mongodb-migration-guide).
-
- We've had customers inquire about the availability of a Rust/C++ SDK. The PowerSync core SQLite extension is implemented in Rust and we can work with customers to integrate PowerSync into their C++ applications. Please reach out to us at hello@powersync.com or [schedule a call](https://www.powersync.com/contact) with us
-
+It is also possible to self-host PowerSync. An end-to-end demo app using Docker Compose is available [here](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs-mongodb).
-#### App Backend
+### 3. Connect PowerSync to MongoDB
-Your app backend needs to expose an API endpoint to write changes from the PowerSync client to the backend MongoDB database.
+Once your account is set up, [create a new PowerSync instance](/installation/database-connection#create-a-powersync-cloud-instance) and configure the instance to connect to your source [MongoDB database](/installation/database-connection#mongodb-beta-specifics).
-As noted above, there are currently two options:
- 1. **Custom self-hosted backend:** If you already have a backend application as part of your stack, you should use your existing backend. If you don't yet have one: We have Node.js, Django and Rails example implementations available [here](/resources/demo-apps-example-projects#custom-backend-examples).
- 2. **CloudCode hosted/managed backend:** An alternative option is to use CloudCode, a serverless cloud functions environment provided by us. We provide a template that you can use as a turnkey starting point. See our [documentation here](/usage/tools/cloudcode).
+### 4. Define Sync Rules
-Additional backend implementation details are available here: [Writing Client Changes](/installation/app-backend-setup/writing-client-changes).
+Sync Rules allow you to control which data gets synced to which users/devices. Each PowerSync Service instance has a Sync Rules definition that consists of SQL-like queries in a YAML file.
+
+To get a good understanding of how Sync Rules operate, have a look at our blog post: [Sync Rules from First Principles: Partial Replication to SQLite](https://www.powersync.com/blog/sync-rules-from-first-principles-partial-replication-to-sqlite).
+
+If you have a PowerSync Service instance set up and connected, open the `sync-rules.yaml` file associated with your PowerSync project and edit the SQL-like queries based on your database schema. Below is a simple Sync Rules example using a simple database schema. Sync Rules involve organizing data into ["buckets"](/usage/sync-rules/organize-data-into-buckets) (a bucket is a grouping of data). The example below uses a ["global bucket"](/usage/sync-rules/example-global-data) as a simple starting point — data in a "global bucket" will be synced to all users.
+
+Note that MongoDB uses “_id” as the name of the ID field in collections whereas PowerSync uses “id” in its client-side database. This is why `SELECT _id as id` should always be used in the data queries when pairing PowerSync with MongoDB.
+
+```yaml
+bucket_definitions:
+ # This is the name of the bucket, in this case the global bucket synced to all users.
+ global:
+ # This is the query used to determine the data in each bucket
+ data:
+ # Note that we select the MongoDB _id field as id
+ - SELECT _id as id, * FROM lists
+ - SELECT _id as id, * FROM todos
+```
+
+To filter data based on the user and other more advanced use cases, refer to the [Sync Rules documentation](/usage/sync-rules).
+
+
+### 5. Add PowerSync to your app
+
+Add PowerSync to your app project by following the instructions for the relevant PowerSync Client SDK.
+
+* Visit our [Client SDK directory](/client-sdk-references/introduction) for instructions specific to your platform.
+
+### 6. Define your client-side schema
+
+The PowerSync client-side schema represents a “view” of the data synced from the PowerSync Service to the client app. No migrations are required — the schema is applied directly when the local PowerSync SQLite database is constructed.
+
+To make this step easy for you, the [PowerSync Dashboard](/usage/tools/powersync-dashboard) allows automatically generating the client-side schema based on the Sync Rules defined for a PowerSync instance. To generate the schema, go to the [dashboard](https://powersync.journeyapps.com/), right-click on the instance, and select “Generate Client Schema”. Alternatively you can use the PowerSync [CLI](/usage/tools/cli) to generate the schema.
+
+Here is an example of a client-side schema for PowerSync using a simple `todos` table:
+
+
+
+ ```typescript TypeScript - React Native
+ import { column, Schema, Table } from '@powersync/react-native';
+
+ const todos = new Table(
+ {
+ list_id: column.text,
+ created_at: column.text,
+ completed_at: column.text,
+ description: column.text,
+ created_by: column.text,
+ completed_by: column.text,
+ completed: column.integer
+ },
+ { indexes: { list: ['list_id'] } }
+ );
+
+ export const AppSchema = new Schema({
+ todos
+ });
+ ```
+
+ ```typescript TypeScript - Web
+ import { column, Schema, Table } from '@powersync/web';
+
+ const todos = new Table(
+ {
+ list_id: column.text,
+ created_at: column.text,
+ completed_at: column.text,
+ description: column.text,
+ created_by: column.text,
+ completed_by: column.text,
+ completed: column.integer
+ },
+ { indexes: { list: ['list_id'] } }
+ );
+
+ export const AppSchema = new Schema({
+ todos
+ });
+ ```
+
+ ```java Kotlin
+ import com.powersync.db.schema.Column
+ import com.powersync.db.schema.Index
+ import com.powersync.db.schema.IndexedColumn
+ import com.powersync.db.schema.Schema
+ import com.powersync.db.schema.Table
+
+
+ val AppSchema: Schema = Schema(
+ listOf(
+ Table(
+ name = "todos",
+ columns = listOf(
+ Column.text('list_id'),
+ Column.text('created_at'),
+ Column.text('completed_at'),
+ Column.text('description'),
+ Column.integer('completed'),
+ Column.text('created_by'),
+ Column.text('completed_by')
+ ),
+ // Index to allow efficient lookup within a list
+ indexes = listOf(
+ Index("list", listOf(IndexedColumn.descending("list_id")))
+ )
+ )
+ )
+ )
+ ```
+
+ ```swift Swift
+ import PowerSync
+
+ let todos = Table(
+ name: ”todos”,
+ columns: [
+ Column.text("list_id"),
+ Column.text("description"),
+ Column.integer("completed"),
+ Column.text("created_at"),
+ Column.text("completed_at"),
+ Column.text("created_by"),
+ Column.text("completed_by")
+ ],
+ indexes: [
+ Index(
+ name: "list_id",
+ columns: [IndexedColumn.ascending("list_id")]
+ )
+ ]
+ )
+
+ let AppSchema = Schema(todos)
+ ```
+
+ ```dart Flutter
+ import 'package:powersync/powersync.dart';
+
+ const schema = Schema(([
+ Table('todos', [
+ Column.text('list_id'),
+ Column.text('created_at'),
+ Column.text('completed_at'),
+ Column.text('description'),
+ Column.integer('completed'),
+ Column.text('created_by'),
+ Column.text('completed_by'),
+ ], indexes: [
+ Index('list', [IndexedColumn('list_id')])
+ ])
+ ]));
+
+ ```
+
+
+
+A few things to note regarding the PowerSync client-side schema:
+
+* The schema does not explicitly specify an `id` column, since the PowerSync automatically creates an `id` column of type `text`.
+* SQLite has very simple data types which are [used by](/usage/sync-rules/types#types) PowerSync.
+* For MongoDB specific data types, refer to [MongoDB Type Mapping](/usage/sync-rules/types#mongodb-beta-type-mapping).
+* PowerSync also supports [syncing attachments or files](/usage/use-case-examples/attachments-files#attachments-files) using helper packages.
+
+
+### 7. Instantiate PowerSync client database
+
+Now that we have our Sync Rules and client-side schema defined, we can instantiate the PowerSync database on the client-side. This will allow the app to start syncing data. For more details, see [Instantiate PowerSync Database](/installation/client-side-setup/instantiate-powersync-database).
+
+
+
+ ```typescript TypeScript - React Native
+ import { PowerSyncDatabase } from '@powersync/react-native';
+ import { Connector } from './Connector';
+ import { AppSchema } from './Schema';
+
+ export const db = new PowerSyncDatabase({
+ schema: AppSchema,
+ database: {
+ dbFilename: 'powersync.db'
+ }
+ });
+
+ export const setupPowerSync = async () => {
+ const connector = new Connector();
+ db.connect(connector);
+ };
+ ```
+
+ ```typescript TypeScript - Web
+ import { PowerSyncDatabase } from '@powersync/web';
+ import { Connector } from './Connector';
+ import { AppSchema } from './Schema';
+
+ export const db = new PowerSyncDatabase({
+ schema: AppSchema,
+ database: {
+ dbFilename: 'powersync.db'
+ }
+ });
+
+ export const setupPowerSync = async () => {
+ const connector = new Connector();
+ db.connect(connector);
+ };
+ ```
+
+ ```java Kotlin
+ // 1: Create platform specific DatabaseDriverFactory to be used by the PowerSyncBuilder to create the SQLite database driver.
+
+ // commonMain
+ import com.powersync.DatabaseDriverFactory
+ import com.powersync.PowerSyncDatabase
+
+ // Android
+ val driverFactory = DatabaseDriverFactory(this)
+ // iOS & Desktop
+ val driverFactory = DatabaseDriverFactory()
+
+ // 2: Build a PowerSyncDatabase instance using the PowerSyncBuilder and the DatabaseDriverFactory. The schema you created in a previous step is provided as a parameter:
+
+ // commonMain
+ // Uses the backend connector that will be created in the next step
+ database.connect(MyConnector())
+
+
+ // 3: Connect the PowerSyncDatabase to the backend connector:
+
+ // commonMain
+ // Uses the backend connector that will be created in the next step
+ database.connect(MyConnector())
+
+ ```
+
+ ```swift Swift
+ let schema = AppSchema
+ let connector = Connector();
+
+ let db = PowerSyncDatabase(
+ schema: schema,
+ dbFilename: "powersync.sqlite"
+ )
+
+ await db.connect(connector: connector);
+ ```
+
+ ```dart Flutter
+ import 'package:powersync/powersync.dart';
+ import 'package:path_provider/path_provider.dart';
+ import 'package:path/path.dart';
+
+ openDatabase() async {
+ final dir = await getApplicationSupportDirectory();
+ final path = join(dir.path, 'powersync-dart.db');
+
+ db = PowerSyncDatabase(schema: schema, path: path);
+ await db.initialize();
+ }
+ ```
+
+
+
+### 8. Reading and writing data
+
+Reading data in the application which uses PowerSync is very simple: we use SQLite syntax to query data in our local database.
+
+
+
+ ```typescript TypeScript - React Native & Web
+ // Reading Data
+ export const getTodos = async () => {
+ const results = await db.getAll('SELECT * FROM todos');
+ return results;
+ }
+ ```
+
+ ```java Kotlin
+ // Reading Data
+ export const getTodos = async () => {
+ const results = await db.getAll('SELECT * FROM todos');
+ return results;
+ }
+ ```
+
+ ```swift Swift
+ // Reading Data
+ func getTodos() async throws {
+ try await self.db.getAll(
+ sql: "SELECT * FROM todos",
+ mapper: { cursor in
+ TodoContent(
+ list_id: cursor.getString(index: 0)!,
+ description: cursor.getString(index: 1)!,
+ completed: cursor.getString(index: 2)!,
+ created_by: cursor.getString(index: 3)!,
+ completed_by: cursor.getString(index: 4)!,
+ completed_at: cursor.getString(index: 5)!
+ )
+ }
+ )
+ }
+ ```
+
+ ```dart Flutter
+ /// Reading Data
+ Future getTodos() async {
+ final result = await db.get('SELECT * FROM todos');
+ return TodoList.fromRow(result);
+ }
+ ```
+
+
+
+The same applies to writing data: `INSERT`, `UPDATE` and `DELETE` statements are used to create, update and delete rows.
+
+
+
+ ```typescript TypeScript - React Native & Web
+ // Writing Data
+ export const insertTodo = async (listId: string, description: string) => {
+ await db.execute('INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)', [listId, description]);
+ }
+ ```
+
+ ```java Kotlin
+ // Writing Data
+ suspend fun insertTodo(listId: String, description: String) {
+ database.writeTransaction {
+ database.execute(
+ sql = "INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)",
+ parameters = listOf(listId, description)
+ )
+ }
+ }
+ ```
+
+ ```swift Swift
+ // Writing Data
+ func insertTodo(_ listId: String, _ description: String) async throws {
+ try await db.execute(
+ sql: "INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)",
+ parameters: [listId, description]
+ )
+ }
+ ```
+
+ ```dart Flutter
+ /// Writing Data
+ await db.execute(
+ 'INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)',
+ ['ID', 'Description'],
+ );
+ ```
+
+
+
+The best way to ensure referential integrity in your database is to use UUIDs when inserting new rows on the client side. Since UUIDs can be generated offline/locally, they allowing for unique identification of records created in the client database before they are synced to the server.
+
+#### Live queries
+
+PowerSync supports "live queries" or "watch queries" which automatically refresh when data in the SQLite database is updated (e.g. as a result of syncing from the server). This allows for real-time reactivity of your app UI. See the [Client SDK documentation](/client-sdk-references/introduction) for your specific platform for more details.
+
+
+### 9. Accept uploads on the backend
+
+MongoDB Atlas Device Sync provides built-in writes/uploads to the backend MongoDB database.
+
+PowerSync offers full custommizability regarding how writes are applied. This gives you control to apply your own business logic, data validations, authorization and conflict resolution logic.
+
+There are two options:
+
+* **Serverless cloud functions (hosted/managed)**: PowerSync offers serverless cloud functions hosted on the same infrastructure as PowerSync Cloud which can used for the needed backend functionality needed. We provide a MongoDB-specific template for this which can be used as a turnkey solution.
+* **Custom self-hosted backend**: Alternatively, writes can be processed through your own backend.
+
+#### Using PowerSync’s serverless cloud functions
+
+PowerSync provides serverless cloud functions for backend functionality, with a template available for MongoDB. See the [step-by-step instructions](/usage/tools/cloudcode) on how to use the template. The template can be customized, or it can be used as-is.
+
+The template provides [turnkey conflict resolution](https://www.powersync.com/blog/turnkey-backend-functionality-conflict-resolution-for-powersync#turnkey-conflict-resolution) which roughly matches the built-in conflict resolution behavior provided by MongoDB Atlas Device Sync.
+
+PowerSync's serverless cloud functions require a bit of "white glove" assistance from our team. If you want to use this option, please [get in touch with us](https://www.powersync.com/contact) so we can get you set up.
+
+For more information, see our blog post: [Turnkey Backend Functionality & Conflict Resolution for PowerSync](https://www.powersync.com/blog/turnkey-backend-functionality-conflict-resolution-for-powersync).
+
+
+#### Using your own custom backend API
+
+This option gives you complete control over the backend. The simplest backend implementation is to simply apply writes to MongoDB as they are received, which results in a last-write-wins conflict resolution strategy (same as the “turnkey backend functionality” option above). See [Writing Client Changes](/installation/app-backend-setup/writing-client-changes) for more details.
+
+On the client-side, you need to wire up the `uploadData()` function in the “backend connector” to use your own backend API. The [App Backend Setup](/installation/app-backend-setup) section of our docs provides step-by-step instructions for this.
+
+Also see the section on [how to set up a simple backend API](https://www.powersync.com/blog/migrating-a-mongodb-atlas-device-sync-app-to-powersync#backend-api-setup) in our practical MongoDB migration [example](https://www.powersync.com/blog/migrating-a-mongodb-atlas-device-sync-app-to-powersync) on our blog.
+
+We also have [example backend implementations](/resources/demo-apps-example-projects#custom-backend-examples) available (e.g. Node.js, Django, Rails)
+
+
+## Questions? Need help?
+
+[Get in touch](https://www.powersync.com/contact) with us.
\ No newline at end of file