Quick Start & Important Rules
Before using the API, keep these fundamental concepts in mind:
Your developer credentials and service access are configured once during onboarding. No periodic re-authentication is required.
You do NOT need to call initRepo() on every app run. Only call it when creating a brand new repository.
Sending transactional email via POST /:uid/mail connects via SMTP socket and requires no Git repository.
Overview & Base URL
Breacher executes on Cloudflare Workers edge nodes, bridging three core subsystems:
JSON documents, collections, and binary assets committed into private GitHub repos with automatic SHA conflict resolution and edge caching.
Connects directly to smtp.gmail.com:465 over raw TLS sockets for instant transactional email delivery.
100,000 round salted PBKDF2 hashing, HMAC indexed email lookups, and HttpOnly SameSite=None secure session cookies.
Breacher JS SDK
Import the zero-dependency Breacher SDK directly into your web applications, scripts, or serverless functions.
// Direct fetch() API (No client class needed)
const BASE = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db";
// 1. User Registration: POST /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/auth/register
const regRes = await fetch(`${BASE}/auth/register`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "user@example.com",
password: "SecurePassword123!",
name: "Alex Doe"
})
});
const regData = await regRes.json();
console.log("Registered:", regData);
// 2. User Login: POST /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/auth/login
const loginRes = await fetch(`${BASE}/auth/login`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "user@example.com",
password: "SecurePassword123!"
})
});
const loginData = await loginRes.json();
console.log("Logged in session:", loginData);Feature Configuration & Domain Restrictions
Breacher verifies developer privileges automatically. You can also configure project-level rules such as authorized browser origins.
authorizedDomains: ["myapp.com"] - Restricts API requests to authorized browser origins.authLimit: 100 - Maximum allowed registered user count for the project.email_signin: false - Restricts sign-in method to OAuth only.Mail API & SMTP Aliases
Connects directly to smtp.gmail.com:465 over raw TLS sockets. Supports rich HTML, fallback plaintext, preview preheaders, and sender aliases. Does not require a GitDB repository.
| Field | Type | Status | Description |
|---|---|---|---|
to | string | Required | Recipient email address (e.g. user@example.com). |
subject | string | Required | Email subject line. UTF-8 Base64 encoded by the worker so emojis and special characters render cleanly. |
from or fromEmailaliasEmail | string | Optional | Custom sender address or configured Google Workspace alias. Default: Your configured google_mail in Firebase |
aliasName or fromName | string | Optional | The display name shown in the recipient inbox (e.g. 'Support Team'). |
alias | string | Optional | Shorthand field: treated as fromEmail if it contains '@', or aliasName otherwise. |
html | string | Optional | Rich HTML email body. Allows inline styles, buttons, and layouts. |
text | string | Optional | Plaintext fallback body if HTML is omitted. |
preview | string | Optional | Inbox preheader text shown in Gmail / Apple Mail message previews. |
// POST /U6op8Nr462Z46tBgxa2COUnja5z1/mail -> Direct fetch to Sockets Gmail SMTP
const res = await fetch("https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/mail", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
to: "customer@example.com",
subject: "Order Confirmation #4092",
from: "orders@myapp.com",
aliasName: "Acme Store",
preview: "Thank you for your purchase!",
html: "<div style='font-family:sans-serif;'><h1>Thank you!</h1><p>Your order is confirmed.</p></div>"
})
});
const data = await res.json();
console.log(data);{
"success": true,
"message": "Email sent successfully",
"detail": "250 2.0.0 OK",
"logId": "d3b07384-d113-4674-8742-88f54128f7bb"
}Initialize GitDB Repository
Creates a private GitHub repository automatically under your connected GitHub account and registers the project. You only need to run this once per new database project.
// Run once when setting up a new database (POST /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/init)
const res = await fetch("https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/init", {
method: "POST"
});
const data = await res.json();
console.log(data);Documents: Create, Read, & Update
Store arbitrary JSON documents at any nested path (e.g., users/usr_123 or settings/global).
Writes or replaces a document. Automatically sets createdAt (if new) and updatedAt ISO timestamps. Handles GitHub 409 SHA conflict retries automatically.
Performs a partial merge with the existing JSON document on GitHub, updating specified fields and refreshing updatedAt.
Retrieves the document. Responses are edge-cached for 60 seconds.
const BASE = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db";
// 1. Create Document (PUT /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/:path)
await fetch(`${BASE}/products/item_99`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Mechanical Keyboard", price: 149.99 })
});
// 2. Read Document (GET /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/:path)
const readRes = await fetch(`${BASE}/products/item_99`);
const product = await readRes.json();
console.log("Product:", product);
// 3. Partial Update (PATCH /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/:path)
await fetch(`${BASE}/products/item_99`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ price: 129.99 })
});Deletion: Files, Folders, & Media
Breacher supports atomic deletion of individual JSON documents, entire folder directories with nested trees in a single Git commit, and binary media assets.
Deletes an exact file path or document. If .json is omitted, Breacher automatically looks up both the raw path and the .json file.
Recursively removes all files and subdirectories under the path atomically using Git Data Tree operations in one single commit.
Deletes binary uploaded assets under the /media/* path prefix and purges Cloudflare edge CDN cache immediately.
A folder deletion is triggered whenever any of the following are present:
- Query parameter:
?type=folderor?type=dir - Query parameter:
?recursive=true - Trailing slash in the URL path (e.g.
DELETE /:uid/:repo/archives/) - Automatic directory detection if GitHub returns a directory tree for the path.
const BASE = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db";
// 1. Delete a single document / file (DELETE /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/:path)
const fileRes = await fetch(`${BASE}/users/usr_101`, { method: "DELETE" });
const fileData = await fileRes.json();
console.log(fileData);
// 2. Delete an entire folder recursively (DELETE /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/:folder?type=folder)
const folderRes = await fetch(`${BASE}/logs/archive?type=folder`, { method: "DELETE" });
const folderData = await folderRes.json();
console.log(`Deleted ${folderData.deletedCount} items:`, folderData.deletedPaths);
// 3. Delete a media asset (DELETE /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/media/:path)
await fetch(`${BASE}/media/uploads/photo.jpg`, { method: "DELETE" });{
"success": true,
"message": "Deleted users/usr_101.json",
"path": "users/usr_101.json",
"type": "file",
"commit": "a3f89012c..."
}{
"success": true,
"message": "Deleted folder 'logs/archive' (14 items removed)",
"path": "logs/archive",
"type": "folder",
"deletedCount": 14,
"deletedPaths": [
"logs/archive/2026-01.json",
"logs/archive/sub/events.json"
],
"commit": "b7e41189d..."
}Collection Queries & Sorting
When querying a directory, Breacher reads all JSON documents in parallel, attaches the document key as _id, and applies server-side sorting and limiting.
| Parameter | Type | Example | Description |
|---|---|---|---|
sort | string | Optional | Sorts documents by a field key and direction formatted as field:asc or field:desc (e.g. createdAt:desc or price:asc). |
limit | integer | Optional | Limits the maximum number of documents returned in the response. |
// Query Collection (GET /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/:collection?sort=field:dir&limit=N)
const res = await fetch("https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/messages?sort=createdAt:desc&limit=25");
const data = await res.json();
console.log(`Found ${data.count} documents:`, data.documents);{
"count": 2,
"path": "messages",
"documents": [
{
"_id": "msg_002",
"text": "Latest message",
"createdAt": "2026-08-26T12:00:00.000Z",
"updatedAt": "2026-08-26T12:00:00.000Z"
},
{
"_id": "msg_001",
"text": "First message",
"createdAt": "2026-08-26T11:00:00.000Z",
"updatedAt": "2026-08-26T11:00:00.000Z"
}
]
}Realtime Database Stream (SSE)
Provides native Server-Sent Events (text/event-stream). You can listen to a Collection (folder) to get an array of all documents, or listen to a Document (file) to get its specific fields. Any standard POST, PUT, or PATCH write to that path automatically triggers a real-time update to all subscribers.
// 1. READ: Listen to a Collection (Folder) stream
const streamUrl = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/stream/messages";
const eventSource = new EventSource(streamUrl, { withCredentials: true });
eventSource.onmessage = (event) => {
// When listening to a folder, the payload is an array of documents
const data = JSON.parse(event.data);
console.log("Live collection update. Total docs:", data.length);
// Example data: [{ _id: "msg_1", text: "Hello", ... }, ...]
};
// 2. WRITE: Trigger a stream update by writing to the folder
// Just use standard HTTP POST/PUT/PATCH to the regular path (without /stream/)
async function sendMessage(text) {
const docId = "msg_" + Date.now();
await fetch(`https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/messages/${docId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: text, // "text" is a field
sender: "Alice", // "sender" is a field
createdAt: new Date().toISOString()
})
});
// The SSE eventSource above will instantly receive the updated array!
}
// Send a test message
sendMessage("Hello from Realtime Stream!");
// To stop listening:
// eventSource.close();- Collections (Folders): If you connect to
/stream/messages, you receive a JSON array of all documents inside themessages/folder. - Documents (Files): If you connect to
/stream/messages/doc1, you receive a JSON object of just that document. - Fields: Are the keys/values inside your JSON document (e.g.
"text": "Hello"). - Triggering Updates: You do NOT POST to
/stream/.... You simply write to the normal endpoint (e.g.,PUT /messages/doc1). The server detects the change and automatically pushes the new data down the stream.
Media & Binary Asset Storage
Upload images, videos, audio, or PDFs up to 10MB directly to your repository. Served with correct MIME headers and long-term CDN caching.
// 1. Upload File (POST /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/media/:path)
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const uploadPath = "uploads/" + Date.now() + "_" + file.name;
await fetch(`https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/media/${uploadPath}`, {
method: "POST",
headers: { "Content-Type": file.type || "application/octet-stream" },
body: file
});
// 2. Direct Public Embed URL (GET /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/media/:path)
const mediaUrl = `https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/media/${uploadPath}`;
console.log("Media URL:", mediaUrl);
// 3. Delete Media Asset (DELETE /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/media/:path)
await fetch(mediaUrl, { method: "DELETE" });User Registration & Login
Breacher includes a full user identity layer backed by GitHub storage. Registration uses an atomic Durable Object coordinator to guarantee unique email claims without race conditions. Passwords are hash-protected with PBKDF2-SHA256 (100,000 iterations). Sessions are stored in HttpOnly cookies.
// Direct fetch calls sending actual HTTP requests to the worker endpoints
const BASE_AUTH = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth";
// 1. REGISTER -> POST https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth/register
const regRes = await fetch(`${BASE_AUTH}/register`, {
method: "POST",
credentials: "include", // CRITICAL for receiving session cookie
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "alice@example.com",
password: "SecurePassword123!",
name: "Alice Developer"
})
});
const regData = await regRes.json();
console.log("Registered user:", regData.user);
// 2. LOGIN -> POST https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth/login
const loginRes = await fetch(`${BASE_AUTH}/login`, {
method: "POST",
credentials: "include", // Saves the HttpOnly session cookie in the browser
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "alice@example.com",
password: "SecurePassword123!"
})
});
const loginData = await loginRes.json();
console.log("Logged in user:", loginData.user);
// 3. LOGOUT -> POST https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth/logout
await fetch(`${BASE_AUTH}/logout`, {
method: "POST",
credentials: "include"
});How to Keep Users Logged In (On-Boot / Refresh Check)
When a user logs in, the worker returns a secure breacher_session cookie (valid for 30 days). To keep the user logged in across page refreshes and browser restarts, send a GET /auth/me request with credentials: "include" when your frontend boots up:
// Standard JavaScript Fetch Pattern (Runs immediately when the page loads)
const AUTH_URL = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth";
async function checkAuthSession() {
try {
// 1. Send GET request including the session cookie
const res = await fetch(`${AUTH_URL}/me`, {
method: "GET",
credentials: "include" // MUST be 'include' to pass cookies across origins
});
if (res.ok) {
const data = await res.json();
console.log("User is currently logged in:", data.user);
// Update UI: show dashboard, hide login form
renderUserDashboard(data.user);
} else {
console.log("No active session or session expired.");
// Update UI: show login/register buttons
renderLoginForm();
}
} catch (err) {
console.error("Auth check failed:", err);
renderLoginForm();
}
}
// Call on window load or DOMContentLoaded
window.addEventListener("DOMContentLoaded", checkAuthSession);- Always specify
credentials: "include": Modern browsers will not transmit cookies to cross-origin API endpoints unless this flag is explicitly set. - Session Expiry & Storage: Sessions are stored directly in your GitHub database under
authentication/sessions/and automatically expire after 30 days. - Logging Out: Calling
POST /auth/logoutwithcredentials: "include"clears the cookie from the browser and destroys the session file on the server.
Session Profile Management
Reads and updates the currently authenticated user profile. The password hash is automatically stripped from the response payload.
const BASE_AUTH = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth";
// 1. Get Current User Profile (GET /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/auth/me)
const meRes = await fetch(`${BASE_AUTH}/me`, { credentials: "include" });
if (meRes.ok) {
const { user } = await meRes.json();
console.log("Logged in as:", user);
}
// 2. Update Profile Name (PATCH /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/auth/me)
await fetch(`${BASE_AUTH}/me`, {
method: "PATCH",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Alice Wonderland" })
});
// 3. Delete Account Permanently (DELETE /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/auth/me)
await fetch(`${BASE_AUTH}/me`, { method: "DELETE", credentials: "include" });Changing Password & Email
Both operations require the user's existing password for verification. Changing email atomically releases the old HMAC email key and claims the new one.
const BASE_AUTH = "https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth";
// 1. Change Password (POST /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/auth/password)
await fetch(`${BASE_AUTH}/password`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
oldPassword: "OldPassword123!",
newPassword: "NewStrongPassword456!"
})
});
// 2. Change Email Address (PATCH /U6op8Nr462Z46tBgxa2COUnja5z1/app-db/auth/email)
await fetch(`${BASE_AUTH}/email`, {
method: "PATCH",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
newEmail: "new.email@example.com",
password: "CurrentPassword123!"
})
});Password Reset Workflow & Built-in Form
When a user submits their email, Breacher generates a 15-minute cryptographically signed token and automatically dispatches an HTML reset email using your configured Gmail SMTP credentials. The link opens a responsive reset page served natively by the worker.
const res = await fetch("https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth/forgot-password", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "user@example.com" })
});
const data = await res.json();
console.log(data.message);GitHub OAuth Sign-In
Enable users to log in or sign up with their GitHub account. The API exchanges the OAuth code, fetches their verified email, sets a session cookie, and triggers BREACHER_AUTH_COMPLETE sync events.
// 1. Fetch GitHub Authorization URL
const resUrl = await fetch("https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth/github/url");
const { url } = await resUrl.json();
// 2. Redirect user to GitHub consent screen
window.location.href = url;
// 3. In your callback handler after redirect with ?code=XYZ:
const code = new URLSearchParams(window.location.search).get('code');
const loginRes = await fetch("https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/auth/github/callback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code })
});
const { user, isNew } = await loginRes.json();
console.log(isNew ? "Signed up!" : "Logged in!", user);Advanced Web Push & Deep Sync
Our push notification system implements a zero-infrastructure, payload-free architecture. Because standard browser push encryption (RFC 8291) is highly temperamental and hard-capped at 4KB, our worker uses push as a lightweight wake-up signal. When a push wakes up the background Service Worker, it fetches the full custom JSON payload directly from your database queue. This bypasses size limits entirely, guarantees cross-browser reliability, and enables next-generation native behaviors.
1. REST API Specification: POST /notify/send
The backend accepts any arbitrary JSON properties and stores them verbatim inside your GitHub queue. The properties below are standard visual handles, but you can send any nesting of keys.
| Field | Type | Role | Description |
|---|---|---|---|
| targetUserId | string | Required | The User ID of the target recipient, or "all" to broadcast to everyone. |
| title | string | Optional | The visual header of the alert. Skip if trigger is silent. |
| body | string | Optional | The descriptive body text of your visual alert. |
| icon | string | Optional | URL of the square thumbnail (e.g., sender profile picture). Can also be a Base64 data URI string. |
| badge | string | Optional | URL of a monochrome logo icon shown in the device status bar on Android. |
| image | string | Optional | URL of a large visual banner / hero graphic displayed inside the expanded notification. |
| silent | boolean | Optional | If set to true, tells the service worker to process data in the background without raising a visual alert. |
| vibrate | number[] | Optional | Vibration rhythm array (e.g., [200, 100, 200]) for haptic feedback. |
| customActions | array | Optional | Interactive button configurations (e.g., [{action: 'accept', title: 'Accept'}]). |
Handling Heavy JSON Lists (Collapsing Chat Floods)
If your backend sends 10 chat messages while a user is offline, standard push triggers 10 noisy beeps. Using our payload-free engine, we fetch the complete list from /notify/latest in one go. Our Service Worker reads the entire queue and merges them into a single visual summary notification.
// Code placed inside sw.js
self.addEventListener("push", (event) => {
event.waitUntil(
(async () => {
const userId = "alice_user_99";
const res = await fetch(`https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/notify/latest?userId=${userId}`);
if (!res.ok) return;
const data = await res.json(); // Array of all pending JSON notifications
const messages = data.notifications || [];
if (messages.length === 0) return;
// If we have only 1 notification, show it directly
if (messages.length === 1) {
const msg = messages[0];
return self.registration.showNotification(msg.title, { body: msg.body, icon: msg.icon });
}
// If we have a heavy list of multiple messages, collapse them beautifully!
const totalCount = messages.length;
const senders = Array.from(new Set(messages.map(m => m.senderName || "Someone"))).join(", ");
await self.registration.showNotification(`${totalCount} New Unread Updates! 📩`, {
body: `You have new updates from ${senders}. Tap to read them all.`,
badge: "/icons/badge.png",
icon: "/icons/chat-group.png",
tag: "collapsed-chat-group", // Prevents multiple notification windows
data: {
deepLinkUrl: "/chat/inbox"
}
});
})()
);
});Custom Banners, Buttons, & Vibration Patterns
Learn how to utilize rich browser handles to create expanded hero banners, specific haptic vibrations, and actionable interactive buttons.
// POST /notify/send body
{
"targetUserId": "alice_user_99",
"title": "Deluxe Pizza Order Dispatched! 🍕",
"body": "Your gourmet order is out for delivery. Estimated arrival: 12 minutes.",
"icon": "https://yourdomain.com/avatar-delivery.png",
"image": "https://yourdomain.com/hero-delivery-map.png", // Large expanded banner
"badge": "https://yourdomain.com/logo-monochrome.png", // Status bar badge icon
"vibrate": [100, 50, 100, 50, 150], // Custom vibration pattern
"customActions": [
{ "action": "track", "title": "Track Live Map" },
{ "action": "cancel", "title": "Cancel Order" }
],
"deepLinkUrl": "/orders/live-tracker"
}// Code inside sw.js
self.addEventListener("push", (event) => {
event.waitUntil(
(async () => {
const res = await fetch(`https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/notify/latest?userId=alice_user_99`);
if (!res.ok) return;
const data = await res.json();
for (const msg of data.notifications) {
await self.registration.showNotification(msg.title, {
body: msg.body,
icon: msg.icon,
image: msg.image, // Renders the large banner
badge: msg.badge, // Android status bar icon
vibrate: msg.vibrate || [200, 100, 200], // Haptics
actions: msg.customActions ? msg.customActions.map(act => ({
action: act.action,
title: act.title
})) : []
});
}
})()
);
});Embedding Visual Assets Natively (Inline Base64 Data)
When you pass simple image URLs, they can fail to load if cellular network coverage is spotty. Because payload-free push triggers a standard HTTP fetch with no size limit, you can embed your visuals directly as Base64 strings. They render instantly and with 100% offline-ready reliability.
// POST /notify/send body containing base64 images
{
"targetUserId": "alice_user_99",
"title": "New Avatar Generated! ✨",
"body": "Your personalized user avatar is ready.",
// Inline Base64 Data URL instead of standard web links:
"icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
}Silent Sync with Active App Windows (Push-to-Sync)
Push signals do not require visual popups. Sending a silent trigger wakes up the Service Worker in the background. It saves the heavy payload data straight to your local databases, then broadcasts a signal to all open tabs. Your active React pages instantly update their component states in real-time with zero page reload or visible spin!
// Listen to background push signals
self.addEventListener("push", (event) => {
event.waitUntil(
(async () => {
const res = await fetch(`https://storage.breacher.name.ng/U6op8Nr462Z46tBgxa2COUnja5z1/my-app-db/notify/latest?userId=alice_user_99`);
if (!res.ok) return;
const data = await res.json();
for (const msg of data.notifications) {
if (msg.silent === true) {
// A. Save heavy payloads directly to IndexedDB or local database
await writeToIndexedDB(msg.syncCollection, msg.syncData);
// B. Broadcast success to all active open tabs
const clientsList = await clients.matchAll({ type: "window" });
for (const client of clientsList) {
client.postMessage({
type: "SILENT_SYNC_COMPLETE",
collection: msg.syncCollection,
updatedData: msg.syncData
});
}
}
}
})()
);
});import { useEffect, useState } from "react";
export default function NotesDashboard() {
const [notes, setNotes] = useState([]);
useEffect(() => {
// 1. Listen for background broadcast signals from the Service Worker
const handleBackgroundSync = (event) => {
if (event.data?.type === "SILENT_SYNC_COMPLETE" && event.data?.collection === "notes") {
console.log("Background synchronization complete! Syncing state...", event.data.updatedData);
// 2. Instantly append synced data straight into state with zero visual latency or spinners!
setNotes((prevNotes) => [event.data.updatedData, ...prevNotes]);
}
};
navigator.serviceWorker.addEventListener("message", handleBackgroundSync);
return () => navigator.serviceWorker.removeEventListener("message", handleBackgroundSync);
}, []);
return (
<div className="p-4 bg-slate-50 border rounded-lg">
<h3 className="font-bold text-sm">Offline-Ready Notes</h3>
<ul className="mt-2 space-y-1 text-xs">
{notes.map(n => <li key={n.id} className="p-2 bg-white border rounded shadow-xs">{n.text}</li>)}
</ul>
</div>
);
}