How a Write Reaches Your Source Database
You implement steps 3 and 4. The client SDK and the PowerSync Service handle the other steps.- When your app writes to a synced table in the client-side SQLite database, the client SDK also adds the write to the upload queue.
- The client SDK calls the
uploadData()function in your backend connector to upload the queued writes. You never calluploadData()directly. See WhenuploadData()is Called. - Your
uploadData()function sends the queued writes to your backend API. After a successful response, your function callscomplete()on the transaction or batch it sent, which removes those writes from the upload queue. For failed requests, see Error Handling. - Your backend API applies the writes to your source database before responding. See Why must my write endpoint be synchronous? for the reason.
- The PowerSync Service replicates the changes from your source database and syncs them to every client whose Sync Streams include the changed rows.
Backend API Design
Since you get to define the client-sideuploadData() 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:
- A single API endpoint that accepts a batch of write operations from the client, with minimal client-side processing.
- Separate API endpoints based on the types of write operations. In your
uploadData(), you can call the respective endpoints as needed. - A combination of the above.
Why must my write endpoint be synchronous?
Why must my write endpoint be synchronous?
PowerSync uses a server-authoritative architecture with a checkpoint system for conflict resolution and 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, SQL Server, or Convex), 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.
Write Operations Recorded on the Client
The upload queue on the client stores three types of operations: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:- Use a batch endpoint to handle high volumes of write operations.
- 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. - For validation errors or write conflicts, avoid returning an error response (
4xx). If youruploadData()function does not callcomplete()after an error response, the client SDK retries the upload indefinitely, which blocks the upload queue. Instead, return a2xxresponse, and if needed, report the validation or other error back to the client, for example by:- Including the error details in the
2xxresponse. - 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).
- Including the error details in the
Handling Write / Validation Errors
Handling Update Conflicts
Example Backend Implementations
See our Example Projects 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 is an easy alternative to a custom backend. Several of our example/demo apps demonstrate how to use Supabase as the backend. These examples use the PostgREST API exposed by Supabase to upload write operations. Alternatively, Supabase’s Edge Functions can also be used.Convex
For Convex, your backend connector applies writes by calling Convex mutations fromuploadData(). Most Convex apps already define mutation functions for each writable table, so you can call those same mutations rather than building a separate API.
If you use Convex Auth tokens directly for PowerSync client authentication, configure PowerSync to accept the convex JWT audience. For self-hosted development this is set in client_auth.audience. For PowerSync Cloud you can configure a custom audience in the instance settings. See Custom Authentication.