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

# Writing Client Changes

> Build a backend API endpoint to apply client-side writes from the PowerSync upload queue to your source database.

<Note>
  Your backend application receives the write operations based on how you defined your `uploadData()` function in the `PowerSyncBackendConnector` in your client-side app. See [Client-Side Integration](/configuration/app-backend/client-side-integration) for details, including [when `uploadData()` is called](/configuration/app-backend/client-side-integration#when-uploaddata-is-called) and its retry behavior.
</Note>

Since you get to define the client-side `uploadData()` function as you wish, you have full control over how to structure your backend application API to accept write operations from the client. For example, you can have:

1. A single API endpoint that accepts a batch of write operations from the client, with minimal client-side processing.
2. Separate API endpoints based on the types of write operations. In your `uploadData()`, you can call the respective endpoints as needed.
3. A combination of the above.

You can also use any API style you want — e.g. REST, GraphQL, gRPC, etc.

<Warning>
  It's important that your API endpoint be blocking/synchronous with underlying writes to the backend source database (Postgres, MongoDB, MySQL, or SQL Server).

  In other words, don't place writes into something like a queue for processing later — process them immediately. For more details, see the explainer below.
</Warning>

<Accordion title="Why must my write endpoint be synchronous?">
  PowerSync uses a server-authoritative architecture with a checkpoint system for conflict resolution and [consistency](/architecture/consistency). The client advances to a new write checkpoint after uploads have been processed, so if the client believes that the server has written changes into your backend source database (Postgres, MongoDB, MySQL, or SQL Server), but the next checkpoint does not contain your uploaded changes, those changes will be removed from the client. This could manifest as UI glitches for your end-users, where the changes disappear from the device for a few seconds and then re-appear.
</Accordion>

### Write Operations Recorded on the Client

The upload queue on the client 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. |

### Recommendations

The PowerSync Client SDK does not prescribe any specific request/response format for your backend application API that accepts the write operations. You can implement it as you wish.

We do however recommend the following:

1. Use a batch endpoint to handle high volumes of write operations.
2. Use an error response (`5xx`) only when the write operations cannot be applied due to a temporary error (e.g. backend source database not available). In this scenario, the PowerSync Client SDK can retry uploading the write operation and it should succeed at a later time.
3. For validation errors or write conflicts, you should avoid returning an error response (`4xx`), since it will block the PowerSync client's upload queue. Instead, it is best to return a `2xx` response, and if needed, propagate the validation or other error message(s) back to the client, for example by:
   1. Including the error details in the `2xx` response.
   2. Writing the error(s) into a separate table/collection that is synced to the client, so that the client/user can handle the error(s).

For details on approaches, see:

<CardGroup>
  <Card title="Handling Write / Validation Errors" icon="code" href="/handling-writes/handling-write-validation-errors" horizontal />
</CardGroup>

For details on handling write conflicts, see:

<CardGroup>
  <Card title="Handling Update Conflicts" icon="code" href="/handling-writes/handling-update-conflicts" horizontal />
</CardGroup>

### Example Backend Implementations

See our [Example Projects](/intro/examples#backend-examples) page for examples of custom backend implementations (e.g. Django, Node.js, Rails, etc.) that you can use as a guide for your implementation.

For Postgres developers, using [Supabase](/integrations/supabase/guide) is an easy alternative to a custom backend. Several of our example/demo apps demonstrate how to use [Supabase](https://supabase.com/) as the backend. These examples use the [PostgREST API](https://supabase.com/docs/guides/api) exposed by Supabase to upload write operations. Alternatively, Supabase's [Edge Functions](https://supabase.com/docs/guides/functions) can also be used.
