-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Description
The official RxDB Supabase replication plugin has two related bugs in the push handler:
push.modifieris never applied - The plugin works directly withrow.newDocumentStatewithout calling the modifier function_modifiedfield is not removed for INSERT operations - It's only removed for UPDATE operations, causing PostgreSQL errors when the field is sent during inserts
Environment
- RxDB Version: 16.20.0
- Plugin:
rxdb/plugins/replication-supabase - Database: Supabase (PostgreSQL) with
moddatetimetrigger for automatic_modifiedmanagement - Platform: React Native (Expo SDK 53)
Current Behavior
When creating a new document (INSERT operation), the plugin sends the _modified field to Supabase:
{
"id": "...",
"amount": 1000,
"_modified": 1761842752620,
"created_at": "2025-10-30T10:05:52.619Z"
}This causes issues because:
- Supabase's
moddatetimetrigger should set_modifiedautomatically - Sending numeric timestamp (milliseconds) causes PostgreSQL error: "date/time field value out of range"
Expected Behavior
The plugin should:
- Apply
push.modifierif provided in configuration - Remove
_modifiedfield for both INSERT and UPDATE operations (currently only UPDATE removes it)
Root Cause
Looking at the plugin source code (node_modules/rxdb/dist/esm/plugins/replication-supabase/index.js):
Bug 1: Push modifier is never called (lines ~109-168)
var replicationPrimitivesPush = options.push ? {
async handler(rows) {
// BUG: modifier is never applied here
await Promise.all(rows.map(async row => {
var newDoc = row.newDocumentState; // Works directly with doc
if (!row.assumedMasterState) {
var c = await insertOrReturnConflict(newDoc); // INSERT
} else {
var _c = await updateOrReturnConflict(newDoc, row.assumedMasterState); // UPDATE
}
}));
}
} : undefined;Fix needed: Apply modifier before calling insert/update functions:
var newDoc = row.newDocumentState;
if (options.push.modifier) {
newDoc = await options.push.modifier(newDoc);
}Bug 2: INSERT doesn't remove _modified (lines ~111-125)
async function insertOrReturnConflict(doc) {
var id = doc[primaryPath];
var { error } = await options.client
.from(options.tableName)
.insert(doc); // BUG: doc contains _modified field
if (error) {
if (error.code === '23505') {
// Duplicate key - fetch current state
return await fetchById(id);
}
throw error;
}
return undefined;
}Compare with UPDATE which correctly removes it (lines ~126-154):
async function updateOrReturnConflict(doc, assumedMasterState) {
var toRow = flatClone(doc);
// CORRECT: modified field will be set server-side
delete toRow[modifiedField];
var query = options.client.from(options.tableName).update(toRow);
// ... rest of update logic
}Fix needed: Remove _modified before INSERT:
async function insertOrReturnConflict(doc) {
var toRow = flatClone(doc);
delete toRow[modifiedField]; // Add this line
var { error } = await options.client
.from(options.tableName)
.insert(toRow); // Use cleaned doc
// ... rest
}Reproduction Steps
- Setup Supabase with
moddatetimetrigger on a table:
CREATE TRIGGER handle_updated_at BEFORE UPDATE ON expenses
FOR EACH ROW EXECUTE FUNCTION moddatetime (_modified);- Configure replication with push modifier:
import { replicateSupabase } from 'rxdb/plugins/replication-supabase';
const replication = replicateSupabase({
collection: myCollection,
client: supabaseClient,
tableName: 'expenses',
replicationIdentifier: 'expenses-sync',
push: {
batchSize: 10,
modifier: (doc) => {
const clean = { ...doc };
delete clean._modified; // Try to remove it
delete clean._rev;
delete clean._meta;
return clean;
}
}
});- Insert a new document:
await myCollection.insert({
id: '123',
amount: 1000,
created_at: new Date().toISOString()
});- Check network request - you'll see
_modifiedis still present despite the modifier
Workaround
Currently using a locally patched version of the plugin with both fixes applied.
Suggested Fix
Apply both fixes to the official plugin:
- In push handler: Apply modifier before calling insert/update
- In
insertOrReturnConflict: Remove_modifiedfield (same as UPDATE does)
This would align the INSERT behavior with UPDATE and properly respect the push.modifier configuration.
Impact
This affects anyone using:
- RxDB Supabase replication with server-side timestamp triggers
- PostgreSQL
moddatetimeextension - Custom
push.modifierfunctions that need to transform documents before sync
The bug causes sync failures and makes it impossible to properly handle timestamp fields that should be set server-side.