site-icon The Average Learner
Jun 8, 2025

Finally Making Offline Sync Working For Appwrite And RxDB

Intro

It wasn’t hard, but I wouldn’t say it’s plug-and-go. It took me a few weeks to make the foundation running correctly, as the major difficult being lacking examples and tutorials. Comparing to Powersync + Supabase or just use Firebase, this definitely requires some set up.

Implementation

There was an official blog post with git exmaple about how to achieve Appwrite + RxDB. However, when I tried it, it was kind of buggy, and it doesn’t do real-time sync. So, I ended up building my own from scratch by reading both Appwrite and RxDB documentations.

Major Hurdles

State management handling with RxDB

I am using LegendState, there are occasions where I try to .set() the instance of my rxdb collection because it causes a loop in my view. So, I have to make sure I use .get() for the rxdb collection, and use LegendState to read and monitor updates. On the other hand, in my viewModel, I have all the functions that manipulate the rxdb directly. The Appwrite replica modification happens in the viewModel functions.

Realtime sync updaing view in loop

Related to the last point. I’m not familiar with LegendState, but I’m getting better now. I added debounce to resolve the issue.

Sync delay messes up local state

Because RxDB Appwrite replication can only perform one write at a time, whenever I performed multiple actions in a short amount of time, the local states have a racing issue. It is because I have subscribed the collection in real-time, yet the data locally changed and received isn’t in sync. Therefore, I created an updatedAt to handle the conflicts.

Deleted fields

You can set deleted field for the replication. It is instructed that to perform deletion using .remove(). However, this delete method would result in no “undo delete”. It is because RxDB hides whatever is soft deleted via its .remove() method, the client cannot access the item just by checking !deleted. There’s a get/findone method for just retrieving a deleted file, kind of troublesome, but I get that rxdb needs its own soft delete mechanism. Therefore, I added an extra deleted field for myself. So, in totoal, there are 3 deleted fields - rxDeleted, isDeleted, deleted(Appwrite default).

Now, when a user deleted an item, I only toggle isDeleted, so it can go to the bin for recovering. On client side, set up a 30 days if isDeleted check to perform rxdb softdelete that toggle rxDeleted. Then, on serverside, perform another check say 30 days of rxDeleted, really delete them.

Soft delete via rxDeleted is necessary for RxDB to sync devices deleted field. But, I also want the user can easily recover the item, so I added isDeleted on top of that.

Conclude

It wasn’t too easy for me, it took me weeks to solve all these, as I started with SwiftUI where Apple CoreData took care every syncing procedures for me. This is the first I actually need to do more for just syncing. But, I’m glad I did it.


Now, I can work on the next big thing!

Previous
Copyright 2025
Sitemap
Privacy Policy
Terms of Service

Theme by Astro-Yi