Offline-first sync primitives for FastAPI and MongoDB applications.
Project description
fastapi-offline-sync
fastapi-offline-sync is a robust, production-ready offline-first sync layer for FastAPI applications backed by MongoDB. It provides high-performance synchronization endpoints and live WebSocket updates with built-in conflict resolution, multi-worker safety, and atomic transactions.
Features
- Incremental Pull: Efficient retrieval of incremental data changes using Hybrid Logical Clocks (HLC).
- Atomic Push Batches: Safely apply client mutations in atomic MongoDB transactions, preventing partial state corruption.
- WebSocket Streaming: Live Change Stream propagation for real-time document updates.
- Distributed Multi-Worker Safety: Custom HLC node namespaces derived automatically or configured via container metadata to eliminate collision risks.
- Soft-Delete Propagation: Seamless tombstones on full resyncs to clear obsolete client-side records.
Installation
pip install fastapi-offline-sync
Quick Start
Initialize and mount the router with production configurations:
import os
from fastapi import FastAPI
from fastapi_offline_sync import SyncConfig, SyncRouter
app = FastAPI(title="Production Sync Service")
config = SyncConfig(
mongodb_uri=os.environ["MONGODB_URI"],
database_name=os.environ["MONGODB_DATABASE"],
collections=("tasks", "inventory_items"),
# Set unique HLC node ID (e.g., container hostname / Pod IP) for multi-worker safety
hlc_node_id=os.environ.get("HOSTNAME"),
)
app.include_router(SyncRouter(config))
Authentication & Identity Resolution
Configure a JWT dependency to verify credentials and return authenticated user properties.
from fastapi import Header, HTTPException, status
async def verify_jwt(
authorization: str | None = Header(default=None),
) -> dict[str, str]:
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing or invalid authentication header",
)
token = authorization.removeprefix("Bearer ")
# Decode and verify the JWT with your auth provider
# Extract identity fields:
return {
"user_id": "user-unique-identifier",
"business_id": "business-tenant-identifier"
}
Then register the dependency with SyncConfig:
config = SyncConfig(
mongodb_uri=os.environ["MONGODB_URI"],
database_name=os.environ["MONGODB_DATABASE"],
collections=("tasks",),
jwt_dependency=verify_jwt,
)
Fisco Integration
For Fisco-style backends where data is scoped by business/tenant rather than individual user, configure the sync engine to scope operations by business_id while keeping the acting identity as user_id.
from fastapi_offline_sync import SyncConfig, SyncRouter, SyncService
config = SyncConfig(
mongodb_uri=os.environ["MONGODB_URI"],
database_name="Fisco",
collections=(
"inventory_items",
"categories",
"orders",
"customers",
"sales",
),
actor_id_field="user_id",
scope_id_field="business_id",
jwt_dependency=verify_jwt,
)
router = SyncRouter(config)
sync_service = SyncService(config)
Server-Layer Writes
For existing service-layer database writes, ensure that you append HLC metadata and oplog entries in the same session:
- Retrieve sync metadata with
sync_service.build_sync_metadata(actor=user, scope_id=business_id). - Persist the metadata fields
_sync_version,_sync_actor_id,_sync_scope_id, and_sync_deletedwith the document. - Record the change using
sync_service.record_server_change(...).
Deploying to Production
Run the application using a production-grade ASGI server:
uvicorn examples.app:app --host 0.0.0.0 --port 8000 --workers 4
API Reference
1. Push Client Changes (POST /sync/push)
Pushes client-side mutations to the server.
curl -X POST https://api.yourservice.com/sync/push \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <JWT_TOKEN>' \
-d '{
"client_id": "device-client-uuid",
"changes": [
{
"collection": "tasks",
"operation": "upsert",
"doc_id": "task-abc-123",
"data": {"title": "Acquire inventory", "done": false},
"parent_version": "20260502T120000.000Z-0001-a1b2"
}
]
}'
2. Incremental Pull (GET /sync/pull)
Fetches operations since a specific HLC token.
curl -H 'Authorization: Bearer <JWT_TOKEN>' \
'https://api.yourservice.com/sync/pull?since=20260502T120000.000Z-0001-a1b2&collections=tasks&limit=100'
3. Full Resync (GET /sync/full)
Triggers a complete sync with Gzip compression and tombstones for soft-deleted documents.
curl -H 'Authorization: Bearer <JWT_TOKEN>' \
--compressed \
'https://api.yourservice.com/sync/full?collections=tasks&limit=1000'
4. Live Updates WebSocket (WS /sync/stream)
Subscribes to live database modifications via a persistent WebSocket connection.
{
"type": "subscribe",
"since": "20260502T120000.000Z-0001-a1b2",
"collections": ["tasks"],
"token": "<JWT_TOKEN>"
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_offline_sync-0.1.1.tar.gz.
File metadata
- Download URL: fastapi_offline_sync-0.1.1.tar.gz
- Upload date:
- Size: 35.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1ed265f9ee1bc0300cc867f6efabfd3f8696e1146166fb3c46bb31e35a7c650
|
|
| MD5 |
8fdb8e1fe0ee78f0f7c7dcd427e51222
|
|
| BLAKE2b-256 |
4a14b177c36af81ad16c7f1b3ebdff34277255950bd53ec6b53927d343173de9
|
File details
Details for the file fastapi_offline_sync-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_offline_sync-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bdc95785788a93b55df157d76c475a1f5a2d7612b72662c4d4218d3546b7f7b
|
|
| MD5 |
36014ae3f5f908538f754c6a2c8a1fef
|
|
| BLAKE2b-256 |
8269bc62e13c7a0def070e78f49f92a946b441e743d550bf70735f3f62893bc0
|