> ## Documentation Index
> Fetch the complete documentation index at: https://powersync-update-diagrams.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing Data

> Use SQL to update client-side SQLite and send queued changes to your application backend.

PowerSync applies each SQL INSERT, UPDATE, or DELETE immediately to client-side SQLite and records the change in an upload queue. Your UI can react to the local result without waiting for a network round trip.

While connected, the SDK calls the `uploadData()` method that you implement in your backend connector. Your implementation sends queued changes (mutations) to your application backend, where you retain control over authorization, validation, and business logic before applying them to your source database. The resulting backend state then syncs to clients through PowerSync. If connectivity is interrupted, writes remain available locally and queued until the SDK can retry them.

## Basic Write Operations

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Insert a new todo
  await db.execute(
    'INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)',
    [listId, 'Buy groceries']
  );

  // Update a todo
  await db.execute(
    'UPDATE todos SET completed = 1, completed_at = date() WHERE id = ?',
    [todoId]
  );

  // Delete a todo
  await db.execute('DELETE FROM todos WHERE id = ?', [todoId]);
  ```

  ```kotlin Kotlin theme={null}
  // Insert a new todo
  database.writeTransaction {
    database.execute(
      sql = "INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)",
      parameters = listOf(listId, "Buy groceries")
    )
  }

  // Update a todo
  database.execute(
    sql = "UPDATE todos SET completed = 1, completed_at = date() WHERE id = ?",
    parameters = listOf(todoId)
  )

  // Delete a todo
  database.execute(
    sql = "DELETE FROM todos WHERE id = ?",
    parameters = listOf(todoId)
  )
  ```

  ```swift Swift theme={null}
  // Insert a new todo
  try await db.execute(
    sql: "INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)",
    parameters: [listId, "Buy groceries"]
  )

  // Update a todo
  try await db.execute(
    sql: "UPDATE todos SET completed = 1, completed_at = date() WHERE id = ?",
    parameters: [todoId]
  )

  // Delete a todo
  try await db.execute(
    sql: "DELETE FROM todos WHERE id = ?",
    parameters: [todoId]
  )
  ```

  ```dart Dart/Flutter theme={null}
  // Insert a new todo
  await db.execute(
    'INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), date(), ?, ?)',
    [listId, 'Buy groceries']
  );

  // Update a todo
  await db.execute(
    'UPDATE todos SET completed = 1, completed_at = date() WHERE id = ?',
    [todoId]
  );

  // Delete a todo
  await db.execute('DELETE FROM todos WHERE id = ?', [todoId]);
  ```

  ```csharp .NET theme={null}
  // Insert a new todo
  await db.Execute(
    "INSERT INTO todos (id, created_at, list_id, description) VALUES (uuid(), datetime(), ?, ?)",
    new[] { listId, "Buy groceries" }
  );

  // Update a todo
  await db.Execute(
    "UPDATE todos SET completed = 1, completed_at = datetime() WHERE id = ?",
    new[] { todoId }
  );

  // Delete a todo
  await db.Execute(
    "DELETE FROM todos WHERE id = ?", 
    new[] { todoId }
  );
  ```
</CodeGroup>

<Note>
  **Best practice**: Use UUIDs when inserting new rows on the client side. UUIDs can be generated offline/locally, allowing for unique identification of records created in the client database before they are synced to the server. See [Client ID](/sync/advanced/client-id) for more details.
</Note>

## ORM Support

PowerSync integrates with popular ORM libraries, which provide type safety and additional tooling. Using an ORM is often preferable to writing raw SQL queries, especially for common operations.

See [ORM Support](/client-sdks/orms/overview) to learn which ORMs PowerSync supports and how to get started.

## Write Operations and Upload Queue

PowerSync automatically queues writes and uploads them to your backend. The upload queue stores three types of operations:

| Operation | Purpose             | Contents                                                 | SQLite Statement                  |
| --------- | ------------------- | -------------------------------------------------------- | --------------------------------- |
| `PUT`     | Create new row      | Contains the value for each non-null column              | Generated by `INSERT` statements. |
| `PATCH`   | Update existing row | Contains the row `id`, and value of each changed column. | Generated by `UPDATE` statements. |
| `DELETE`  | Delete existing row | Contains the row `id`                                    | Generated by `DELETE` statements. |

The SDK automatically processes this queue by calling your `uploadData()` function. For a detailed breakdown of when and how `uploadData()` is called, including triggers, throttling, retry behavior, and error handling, see [When `uploadData()` is Called](/configuration/app-backend/client-side-integration#when-uploaddata-is-called).

For details on how to structure your backend to accept these writes, see [Writing Client Changes](/handling-writes/writing-client-changes).

## Advanced Topics

* [Usage Examples](/client-sdks/usage-examples) - Code examples for common use cases
