Availability: Custom checkpoints are available for customers on our Team and Enterprise plans.
uploadData() returns, so the uploaded changes must already be in the source database at that moment.
Some backends cannot process uploads synchronously. In a chained data pipeline, uploads first go to a queue or an intermediate database and reach the source database later. With the default checkpoints, this makes client data flicker:
- The client uploads a change. Your backend accepts it and queues it, and
uploadData()returns. - The client obtains a write checkpoint. The PowerSync Service marks the current source database position, which does not include the queued change.
- The client receives that checkpoint and applies the server state. The change is missing, so the client reverts it locally.
- The pipeline writes the change to the source database. The Service syncs it, and the client applies it again.
Choosing a Flow
PowerSync supports two custom checkpoint flows. Use custom checkpoint requests for new implementations.
Custom checkpoint requests are part of the Checkpoint Requests API. See that page for the supported client SDKs. The legacy flow continues to work and is documented in Legacy Custom Write Checkpoints.
From PowerSync Service 1.26.0, a Sync Config can define only one of the two events. If you have app versions in production that use the legacy flow, see Migrating to Custom Checkpoint Requests.
Setting Up Custom Checkpoint Requests
With custom checkpoint requests, the PowerSync Client SDK generates an increasing checkpoint request ID and sends it to your backend after each upload. Your backend writes the ID into a checkpoints table in the source database. When the Service replicates the record, the client knows that its uploads are in the source database.1
Create a Checkpoints Table
Create a table in your source database that stores the latest checkpoint request ID for each PowerSync client:
user_idis the authenticated user.client_idis the PowerSync client ID. Each local database has its own client ID, so one user can have many clients.checkpointis the checkpoint request ID. IDs are 64-bit integers, so use aBIGINTor equivalent column.
2
Replicate the Table
For Postgres, add the table to the PowerSync publication:For other source databases, the Service replicates every table that your Sync Config references, including tables in event definitions. Complete the same table setup as for your other tables, such as enabling CDC for a SQL Server table.
3
Add the Event Definition
Add a Use aliases if your column names differ, for example
checkpoint_requests event definition to your Sync Config. Its payload query must return the fields user_id, client_id, and checkpoint:SELECT owner AS user_id, device AS client_id, request_id AS checkpoint FROM checkpoints.4
Add a Backend Endpoint
Add an endpoint that receives the client ID and checkpoint request ID from the client. Take the user ID from your session or token. The endpoint must:Return the value as a string in JSON to avoid precision loss in JavaScript clients. See Checkpoint Request IDs for the full reconciliation rules.
- Store the greater of the submitted ID and the stored ID for that user and client.
- Return that value. If the submitted ID was stale, the client uses the returned ID to continue counting from there.
5
Update the Client
Connect with checkpoint requests enabled and add
postCheckpointRequest() to your backend connector to call your endpoint. The SDK calls this method after each upload, so uploadData() needs no checkpoint handling of its own. See Prerequisites and Connector Changes for how to declare the method in each SDK.Record Retention
The Service keeps a replicated checkpoint request forcheckpoint_request_retention_minutes after it stores the record. The default is 60 minutes. The next compact job then removes it. Clients send their current request ID again when they reconnect, so an expired record is recreated when a client still needs it. On self-hosted instances, you can change the period with api.parameters.checkpoint_request_retention_minutes.
Retention applies only to the Service’s copy. Rows in your checkpoints table are yours to keep or delete. While a row exists, return its value from your endpoint so that a reconnecting client can resume from it.
Legacy Custom Write Checkpoints
In the legacy flow, your backend generates an increasing checkpoint number for each client, and the client passes that number totransaction.complete() after each upload. The Service retains these records because legacy clients wait for a specific number and do not request it again.
1
Create and Replicate a Checkpoints Table
Use the same table and replication setup as for custom checkpoint requests.
2
Add the Event Definition
Add a
write_checkpoints event definition to your Sync Config:3
Add a Backend Endpoint
Add an endpoint that increments and returns the checkpoint number for the user and client. Write the record through the same pipeline as the uploads. For Postgres:
4
Complete Transactions With the Checkpoint
In
uploadData(), request a checkpoint from your backend after uploading the transaction and pass it to complete():Migrating to Custom Checkpoint Requests
While you roll out an updated app version, older versions that still use the legacy flow write legacy checkpoint numbers while updated versions write checkpoint request IDs. Because a Sync Config cannot define bothwrite_checkpoints and checkpoint_requests, support both kinds of records by defining only checkpoint_requests and adding an is_legacy field to the payload. The field is available since Service version 1.26.0.
- Set
is_legacytotruefor legacy records. The Service retains them. - Omit
is_legacy, or set it tofalse, for checkpoint request records. The Service can expire them.
checkpoint_requested_at timestamp that the legacy endpoint leaves NULL:
is_legacy field. With storage version 4, changing an event definition reprocesses only that event’s data.
Example Implementations
- Swift custom checkpoint demo: a client that uses custom checkpoint requests with the Node.js backend demo.
- Node.js backend demo: implements both a checkpoint request endpoint and a legacy checkpoint endpoint, with Postgres, MongoDB, and MySQL persistence.
- Self-hosted custom checkpoints demo: a Docker Compose setup with Postgres that uses the legacy flow.