- DATE:
- AUTHOR:
- PowerSync Product Team
Sync Catch-Up with Checkpoint Requests (Alpha)
With the Checkpoint Requests API your app can now catch up with the server on demand: you request a checkpoint of the current server state, then wait until the local database has caught up to it. Waiting covers uploads as well as downloads: a request created after local writes confirms that those writes have been uploaded and their results have synced back.
let checkpoint = try await database.requestCheckpoint()
try await checkpoint.waitForSync(timeout: 30)
// Local queries now reflect server state from when the request was made.Checkpoint requests are coming to all our SDKs, starting with the Swift SDK: they're available now in v1.16.0 as an alpha release. Support in the JS SDKs is already in progress, with our other SDKs to follow soon.
Why checkpoint requests
PowerSync syncs continuously in the background, and SyncStatus fields like downloading and hasSynced tell you that this is happening — but not whether the data on the device is up to date with the server right now. waitForFirstSync() only covers the initial sync, and workarounds like forcing a reconnect don't confirm that the device has actually caught up. A checkpoint request lets you wait until the local database has caught up with the server as of the moment the request was made.
This need shows up in many common flows:
Critical operations: confirm that pending local writes have uploaded and the latest server data has downloaded before a user starts a work session, or before the app performs a sensitive operation.
Pull-to-refresh: resolve the refresh indicator once the device has caught up with the server, instead of hiding it after an arbitrary delay.
Data availability: when a user opens a link or notification, wait until the data it refers to has synced before rendering the screen.
Backend processing: after your backend finishes a job and writes the result to the source database, know when that result is available locally.
App startup or foregrounding: show a "syncing latest changes" state that ends exactly when the device is caught up.
On-demand sync: not every app needs an always-on connection. If eventual sync is enough, connect, wait for a checkpoint request to sync, then disconnect again.
How they work
During normal sync, the PowerSync Service groups changes from the source database into checkpoints, and the client SDK applies each checkpoint to the local database as a single consistent unit. Checkpoint requests add a way to point at a specific checkpoint: one that reflects server state at or after the moment you asked.
When you call requestCheckpoint(), the SDK posts a request to the PowerSync Service, which records the source database's current replication position. waitForSync() then resolves once a checkpoint covering that position has been fully synced and applied to the local database. At that point, everything the source database contained when the Service handled the request is present on the device. Your own writes are still protected automatically along the way: PowerSync never applies a checkpoint while local writes are waiting to upload, so sync can't revert your pending changes.
Using checkpoint requests
Checkpoint requests are opt-in while the feature is in alpha, and will be enabled by default in a future release. The examples here use Swift, the first SDK to ship the feature and the API should look similar in our other SDKs.
Connect with checkpointMode set to .requests(), then create requests as needed:
try await database.connect(
connector: connector,
options: ConnectOptions(checkpointMode: .requests())
)
let checkpoint = try await database.requestCheckpoint()
try await checkpoint.waitForSync(timeout: 30)The Sync Catch-Up docs cover the full API, error handling, and the relationship to local writes.
If you use custom write checkpoints
If your backend queues uploads instead of committing them synchronously, you may be using custom write checkpoints today. That flow keeps working until you enable checkpoint requests. Once you do, use custom checkpoint requests instead: the source-side setup stays the same, but the request ID now comes from the client SDK. Implement CustomCheckpointRequestConnector on your connector to forward it to your backend. See Asynchronous Upload Backends in the docs for the details.
Reworked internals
Read this section if you're curious about how we implemented this, and which of the inner workings changed.
Checkpoint requests are a generalization of write checkpoints, the mechanism PowerSync already used to make sure your uploaded writes are included before new server state is applied. The feature's design is public in two proposals (checkpoint request protocol changes and explicit sync), but the short version:
Clients now generate their own request IDs. The Service used to hand out write checkpoint IDs; clients now track their own monotonically increasing IDs, which makes requests idempotent and safe to retry. Both flows share one ID namespace, so they stay compatible. (powersync-sqlite-core #198)
Request records now expire. Write checkpoint records could never be cleaned up, so apps with lots of anonymous or short-lived clients accumulated them forever on the Service. Checkpoint request records are deleted by the existing compact job after a retention period — 60 minutes by default, configurable for self-hosted deployments via
api.parameters.checkpoint_request_retention_minutes. (powersync-service #696)There's a new Service endpoint. Clients create requests through
/sync/checkpoint-request, but the request still travels through the existing write checkpoint marker in the sync protocol, so it works with any checkpointing method.Client-side state moved out of
ps_buckets. The SQLite core used to track write checkpoint state in a synthetic$localrow inps_buckets. It now lives in dedicatedps_kvkeys, with the lifecycle managed throughpowersync_controlcommands like the rest of the sync interface.
All of this is opt-in per connection while the feature is in alpha: the default is still the legacy write checkpoint behavior.
Alpha status and requirements
Checkpoint requests are an alpha API and may change based on your feedback. Using them today requires:
PowerSync Service v1.24.0 or later
PowerSync Swift SDK v1.16.0 or later
Support in the JS SDKs is in progress, and our other SDKs will follow. To share feedback, join us on Discord or comment on the explicit sync proposal.