- DATE:
- AUTHOR:
- PowerSync Product Team
Announcing Convex Backend Support (Experimental)
Convex can now be used as a source for PowerSync, available in an experimental release. It's intended for early testing against your use cases and overall feasibility. Your feedback will shape the direction of this integration 🫡
Why Convex
Over the past few months, we've seen a steady uptick in community requests to support Convex. If you haven't yet used it: Convex is a backend platform where you define your schema and write your business logic as TypeScript functions that run against a reactive database. Mutations are ACID transactions, queries are reactive so subscribed clients re-render when their data changes, and the runtime is open source and self-hostable.
Adding PowerSync to a Convex app gives you offline-first behavior, optimistic updates, and partial sync. Your app reads and writes against a local SQLite database, so it stays responsive when the network is slow or absent. Writes appear instantly because they apply to the local database first, then upload to Convex in the background. Each client only syncs the subset of Convex data it needs, so you don't sync your whole database to every device. Your existing Convex mutations still handle writes to the backend.
Architecture
PowerSync has two main pieces: the PowerSync Service that connects to your source database (Convex in this case), and a client SDK that maintains a local SQLite database on each device. The PowerSync Service watches Convex and streams the rows each client should see, based on the Sync Streams you define. The client SDK exposes that data to your app as local SQLite, and queues client writes for upload back to your Convex mutator functions.
The PowerSync Service is built so each source database plugs in as its own module on top of a shared core. The Convex module replicates from Convex's Streaming Export API, maps Convex types to SQLite types, and manages replication checkpoints. Convex being open source helped, since the Streaming Export API is was well-documented and easy to build against.
Initial replication takes a single Convex snapshot of every selected table at the same consistent point. After that, streaming replication polls the global document_deltas endpoint and filters rows based on your Sync Streams.
Convex mutations are ACID transactions, and all writes within a single mutation share a commit timestamp. PowerSync replicates them together as one atomic batch, so clients never see a partial result from a single mutation.
PowerSync clients authenticate to the Service using JWTs. If your app uses Convex Auth, you can pass Convex Auth tokens directly as the JWT. You don't need to run a separate auth service for sync.
How to get started
1. Prepare your Convex deployment
PowerSync works with both Convex Cloud and self-hosted Convex deployments. On the Convex side you need to:
Add a
powersync_checkpointstable to your Convex schema. This is required because PowerSync guarantees causal consistency that spans SQLite and Convex.Deploy a
powersync_checkpoints:createCheckpointmutation that PowerSync calls after recording a write checkpoint.Generate a Convex deploy key for the deployment you want to replicate.
The exact schema and mutation snippets are in the Convex Source Database Setup docs.
2. Connect Convex to PowerSync
PowerSync Cloud: add your Convex deployment URL and deploy key in the PowerSync Dashboard. See Convex specifics.
Self-hosted PowerSync: add a convex connection to your service.yaml. See Self-Hosted Instance Configuration.
Then define Sync Streams to control which Convex tables and rows sync to each client.
3. Set up the client
Follow the Setup Guide for client SDK installation, defining your local schema, and wiring up the upload path. For Convex, your upload path calls your existing Convex mutations directly.
Demo app
For an end-to-end working example, see the PowerSync + Convex To-Do List Demo. It's a React app that uses Convex Auth and calls Convex mutations directly, so no separate backend application is required.
The data flow: mutations start as local SQLite writes in the client. PowerSync queues those writes, the connector uploads them to Convex mutations, Convex updates the backend tables, and PowerSync streams the resulting changes back down to subscribed clients.
Known limitations and caveats
Schema changes: Convex field additions, removals, and type changes flow through as normal document mutations and don't require a re-snapshot. Deleting a table from the Convex Dashboard, however, doesn't emit per-document delete events in
document_deltas, so previously replicated rows can stay on clients. Use the dashboard's "Clear Table" action before deleting a table, or delete documents through mutations. See Convex schema change handling for the full details.ID mapping: PowerSync clients generate UUIDs locally before writes are uploaded, so your Convex mutations need to map between client UUIDs and Convex
_idvalues. This follows the Sequential ID Mapping pattern used with our other backends.Type mapping: Convex
Int64values sync as base-10 text andBytesvalues as base64 text. To sync a ConvexInt64as a SQLite integer, cast it explicitly in your Sync Streams withCAST(value AS INTEGER).Latency: PowerSync polls the Convex
document_deltasendpoint every 1000ms. This interval isn't currently configurable on PowerSync Cloud. On self-hosted PowerSync you can lower it via thepolling_interval_msconnection parameter for reduced latency, at the cost of higher request load on Convex.Replication metrics:
ROWS_REPLICATEDandTRANSACTIONS_REPLICATEDare reported.DATA_REPLICATED_BYTESandCHUNKS_REPLICATEDaren't implemented for Convex yet.
Feedback and help
The Convex connector is released as an experimental feature. APIs and behavior may still change, and we can't yet guarantee continued support or long-term stability. This release is intended for early testing and to invite feedback. Your feedback will directly influence whether, and how, this integration evolves.
Try it, and tell us what works for you and what doesn't. Please open an issue on GitHub or join us in Discord to share your feedback or get help.