Quick Start
import { Tabs, TabItem, Steps } from ‘@astrojs/starlight/components’;
Quick Start
Section titled “Quick Start”Get a two-node active-active SQLite replication cluster running in 5 minutes.
Prerequisites
Section titled “Prerequisites”- Go 1.21+ (for Go runtime) — go.dev/dl
- Bun 1.0+ (for Bun runtime) — bun.sh
- Node.js 18+ (for Node runtime) — nodejs.org
Pick one runtime. All three sync to each other — you can mix and match.
-
Build the binary
Terminal window git clone https://github.com/maulanashalihin/hook-sync.gitcd hook-sync/gogo build -o ../hook-sync-go ./cmd/server -
Start node A (port 9001)
Terminal window ./hook-sync-go -id node1 -db node1.db -listen :9001 -peer http://localhost:9002 -
Start node B (port 9002)
Terminal window ./hook-sync-go -id node2 -db node2.db -listen :9002 -peer http://localhost:9001 -
Write to node A
Terminal window curl -X POST http://localhost:9001/api/items \-H 'Content-Type: application/json' \-d '{"name":"hello","value":42}' -
Verify on node B
Terminal window curl http://localhost:9002/api/items# [{"id":"...","name":"hello","value":42,...}]
-
Install the library
Terminal window bun add hooksync.js -
Create
server.tsimport { attach } from "hooksync.js";import { Database } from "bun:sqlite";const db = new Database("app.db");db.exec("PRAGMA journal_mode = WAL");db.exec(`CREATE TABLE IF NOT EXISTS items(id TEXT PRIMARY KEY, name TEXT, value INTEGER,created_at INTEGER, updated_at INTEGER);`);const mgr = attach(db, {id: "node1",peers: ["http://localhost:9002"],batchMs: 50,}, ["items"]);Bun.serve({port: 9001,fetch(req) {const url = new URL(req.url);if (req.method === "POST" && url.pathname === "/sync") {return req.json().then((body) => {const applied = mgr.applyChanges(body.changes);return Response.json({ applied, ack: body.batch_id });});}if (req.method === "GET" && url.pathname === "/health") {return Response.json(mgr.health());}return new Response("not found", { status: 404 });},}); -
Run two instances
Terminal window bun server.ts # node1 on :9001# In another terminal, change port to 9002 and peer to 9001
-
Install the library
Terminal window npm install hooksync.js better-sqlite3 -
Create
server.jsconst { attach } = require("hooksync.js");const Database = require("better-sqlite3");const http = require("http");const db = new Database("app.db");db.pragma("journal_mode = WAL");db.exec(`CREATE TABLE IF NOT EXISTS items(id TEXT PRIMARY KEY, name TEXT, value INTEGER,created_at INTEGER, updated_at INTEGER);`);const mgr = attach(db, {id: "node1",peers: ["http://localhost:9002"],batchMs: 50,}, ["items"]);const server = http.createServer(async (req, res) => {if (req.method === "POST" && req.url === "/sync") {const body = JSON.parse(await readBody(req));const applied = mgr.applyChanges(body.changes);res.writeHead(200, { "Content-Type": "application/json" });res.end(JSON.stringify({ applied, ack: body.batch_id }));return;}if (req.method === "GET" && req.url === "/health") {res.writeHead(200, { "Content-Type": "application/json" });res.end(JSON.stringify(mgr.health()));return;}res.writeHead(404);res.end("not found");});server.listen(9001); -
Run two instances
Terminal window node server.js # node1 on :9001# In another terminal, change port to 9002 and peer to 9001
Table Requirements
Section titled “Table Requirements”Every synced table must have:
id—TEXT PRIMARY KEY(UUID, zero conflict)updated_at—INTEGER(millisecond timestamp, for last-write-wins)
CREATE TABLE items( id TEXT PRIMARY KEY, name TEXT, value INTEGER, created_at INTEGER, updated_at INTEGER);What Happens Automatically
Section titled “What Happens Automatically”When you call attach() (JS) or trigger.Attach() (Go), the library:
- Creates
_meta,_changes,_dead_letter,_peer_statetables - Auto-generates INSERT/UPDATE/DELETE triggers via schema introspection
- Starts a background ship loop (50ms default)
- Handles ACK-based retry with exponential backoff
- Manages per-peer watermarks for multi-peer topologies
You only write CRUD endpoints + wire POST /sync to mgr.applyChanges().