Security News
Bun 1.2 Released with 90% Node.js Compatibility and Built-in S3 Object Support
Bun 1.2 enhances its JavaScript runtime with 90% Node.js compatibility, built-in S3 and Postgres support, HTML Imports, and faster, cloud-first performance.
sqlite-replication
Advanced tools
A Typescript module to replicate SQLite DB with server.
sqlite-replication
was designed for collaborative offline-first mobile app built with capacitor sqlite plugin.
npm install sqlite-replication --save
// init your SQLite db as usual
const db = ...
const storage = new ReplicationSQLiteStorage(db);
const replicationService = new ReplicationService(storage,
{
// Provide collections descriptor to replicate
collections: [
// use a default helper
getDefaultCollectionOptions(db, 'users'),
// or define your own config
{
name: 'todos',
batchSize: 99,
countDocumentsUpdatedAt: async (updatedAt: number) => { ... },
upsertAll: (documents: any[]) => { ... },
deleteAll: (documents: any[]) => { ...},
}
],
// Define how to pull data from your own server (Rest, GraphQL... up to you)
fetchPull: async (pullConfig: any) => (await api.post(`${URL_BASE}replicationPull`, pullConfig)).data,
// Define how to push data to your server
fetchPush: async () => (await api.post(`${URL_BASE}replicationPush`, {})).data,
}
);
// init checkpoint table at app startup time
await replicationService.init();
// run replication (push then pull) when required, as often as you need
await replicationService.replicate();
//Enjoy SQL query as usual
Ensure your tables have at least these columns :
id
as primary key (UUID recommended)updateAt
as timestamp, the last document change datedeletedAt
as timestamp, the deletion date (if deleted)_forkParent
as text, used for conflict handling.id
updateAt
deletedAt
_forkParent
is not required in server storage and could be removeddeletedAt
timestamp insteadORDER BY "updatedAt", "id"
id
type and filled in a compatible way with distributed context (prefer UUID or similar)const mobilePullBodySchema = {
body: {
type: 'object',
properties: {
// Map of collections to pull
collections: {
type: 'object',
properties: {},
// additional properties names should match SQLite table name
additionalProperties: {
type: 'object',
properties: {
// The cursor (the last already pulled document updatedAt property, as integer)
cursor: { type: 'number', format: 'int64' },
// The number of documents to pull
limit: { type: 'number', format: 'int64' },
/*
* offset N documents.
* `updatedAt` should be the last date the document changes. Its use as cursor.
* But in case of multiple updates in a single query, several documents could have the same updatedAt value.
* This use-case breaks the cursor use and the paginating feature.
* As workaround, we use this parameter to know how much documents with the cursor value has already been pulled and paginate correctly.
*/
offset: { type: 'number', format: 'int64' },
},
required: ['cursor', 'limit', 'offset'],
},
},
},
required: ['collections'],
},
};
app.post('/replicationPull', validate(mobilePullBodySchema), async (req: APIRequest, res) => {
const collections = {};
// Query users only if required
if ( req.body.collections.users ) {
const { cursor, offset, limit } = req.body.collections.users;
const users = await database.select(
'id',
'name',
'updatedAt',
'deletedAt',
)
.from('users')
// pay attention to predictability for paginating
.orderBy(['updatedAt', 'id'])
// get only changed users since the last updateAt value (>=)
.where(database.knex.raw(`date_trunc('milliseconds',"updatedAt")`), '>=', cursor)
// get the page data +1 to know if there is one more page next in a single sql query
.limit(limit + 1)
// skip the N first document with asked updateAt
.offset(offset);
}
collections.users = {
// escape the last document if the page limit is reach (due to limit+1)
documents: users.length > limit ? users.slice(0, -1) : users,
// define if there is a next page
hasMoreChanges: users.length > limit,
cursor,
offset,
limit,
});
}
res.json({ collections });
});
router.post('/replicationPush', validate(replicationPushSchema), async (req: APIRequest, res) => {
const collections = req.body.collections;
if (collections.users) {
await Promise.all(collections.users.map(pushUser));
}
res.json('ok');
});
async function pushUser(user) {
const forkParent = user._forkParent;
delete user._forkParent;
// remotely created case (insert), not that `id` is filled by client
if (forkParent.updatedAt === null) {
// insert in DB, note that `updateAt` default value is now()
return usersService.create(user)
}
// remotely deleted case (delete)
else if (user.deletedAt) {
// no deletion just flag the document with `deletedAt`=now() and `updateAt`=now() too.
return usersService.update({...user,updateAt:Date.now()});
}
//remotely update case
else if (forkParent.updatedAt !== user.updatedAt) {
const serverDocument = await usersService.getById(user.id),
// deleted on server (ignore remote update)
if (!serverDocument || serverDocument.deletedAt) {
return;
}
// no conflict case
else if (serverDocument.updatedAt === forkParent.updatedAt) {
return usersService.update({...user,updateAt:Date.now()});
} else {
const remoteChangedKeys = Object.keys(user).filter(
(key) => JSON.stringify(user[key]) !== JSON.stringify(forkParent[key]),
);
const serverChangedKeys = Object.keys(user).filter(
(key) => JSON.stringify(serverDocument[key]) !== JSON.stringify(forkParent[key]),
);
// handle conflict according to business rules
...
}
}
}
capacitor-community/sqlite UpgradeDatabaseVersion works well and is provided by SQLite itself.
FAQs
A Typescript module to replicate SQLite DB with server.
The npm package sqlite-replication receives a total of 21 weekly downloads. As such, sqlite-replication popularity was classified as not popular.
We found that sqlite-replication demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Bun 1.2 enhances its JavaScript runtime with 90% Node.js compatibility, built-in S3 and Postgres support, HTML Imports, and faster, cloud-first performance.
Security News
Biden's executive order pushes for AI-driven cybersecurity, software supply chain transparency, and stronger protections for federal and open source systems.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.