npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@docstack/pouchdb-adapter-googledrive

v0.2.0

Published

PouchDB adapter for Google Drive

Readme

npm Docs License

PouchDB Adapter for Google Drive

A persistent, serverless PouchDB adapter that uses Google Drive as a backend storage. Designed for high concurrency, large datasets (via lazy loading), and offline resilience.

Part of DocStack: it is what makes a DocStack stack sync to the user's own Drive, so an application gets multi-device sync and real backup with no server, no storage bill and no custody of anyone's data. It works with plain PouchDB just as well.

Features

  • 🚀 Append-Only Log: Uses an efficient append-only log pattern for fast, conflict-free writes.
  • ⚡ Lazy Loading: Optimizes memory and bandwidth by loading only the Index into memory. Document bodies are fetched on-demand.
  • 🛡️ Multi-writer safe: Metadata writes merge rather than replace, and are read back before anything is deleted. See Concurrent writers.
  • 📦 Auto-Compaction: Automatically merges logs for performance.
  • 🌍 Universal: Works natively in Node.js 18+, Browsers, and Edge environments (no googleapis dependency).

Requirements

  • Node.js 18+ (for global fetch support) or a modern browser.

Installation

npm install @docstack/pouchdb-adapter-googledrive

Usage

The adapter is initialized as a plugin with your Google Drive access token.

import PouchDB from 'pouchdb-core';
import GoogleDriveAdapter from '@docstack/pouchdb-adapter-googledrive';

// 1. Initialize the Adapter Plugin Factory
const adapterPlugin = GoogleDriveAdapter({
  accessToken: 'YOUR_GOOGLE_ACCESS_TOKEN',
  folderName: 'my-app-db-folder', // Root folder in Drive
  pollingIntervalMs: 2000         // Optional: check for remote changes
});

// 2. Register Plugin
PouchDB.plugin(adapterPlugin);

// 3. Create Database
const db = new PouchDB('user_db', {
  adapter: 'googledrive'
});

await db.post({ title: 'Hello World' });

Dynamic Tokens

If your token expires, you can provide an async function that returns a valid token:

const adapterPlugin = GoogleDriveAdapter({
  accessToken: async () => {
    const session = await getMySession();
    return session.accessToken;
  },
  folderName: 'my-app-db'
});

Live changes from other clients

Set pollingIntervalMs to have the adapter watch _meta.json and replay what other clients write. Without it, a client only hears about its own writes — connect-and-read works, continuous sync between two connected clients does not.

const adapterPlugin = GoogleDriveAdapter({
  accessToken: 'YOUR_GOOGLE_ACCESS_TOKEN',
  folderId: 'my-folder-id',
  pollingIntervalMs: 2000  // check for remote writes every 2s
});

db.changes({ live: true, since: 'now' }).on('change', change => { /* ... */ });

A tick costs one files.list, whatever has changed; only a tick that sees a new md5Checksum (or, failing that, a new modifiedTime) goes on to fetch anything. db.close() and db.destroy() stop it.

Sync progress

Two signals, one per phase where a UI would otherwise show nothing:

Replication progress rides PouchDB's standard pipeline. The adapter's changes feed reports pending (the CouchDB field), so replication change events and activeTasks carry it without any adapter-specific wiring:

const rep = PouchDB.replicate(remote, local);
rep.on('change', info => {
  const done = info.docs_written;
  const pct = Math.round(done / (done + info.pending) * 100);
  // Freeze the denominator at cycle start, or clamp the bar monotone —
  // other devices keep writing, so `pending` can grow mid-cycle.
});

Do not derive progress from update_seq arithmetic: sequence numbers are sparse (they carry a writer slot in the low digits), so ratios of them mean nothing.

Connect progress covers the cold load, where a busy folder replays dozens of change logs before the database is usable:

GoogleDriveAdapter({
  accessToken: '...',
  folderId: 'my-folder-id',
  onSyncProgress: ({ phase, done, total }) => {
    // phase 'replay': applying change logs, done of total for this load.
  }
});

The callback is fire-and-forget: it always reaches total even when a log download fails (that log is retried on a later load), and an exception it throws cannot fail the load.

Concurrent writers

Several clients may share one folder. What that costs, and what it does not:

  • Writes from different clients do not overwrite each other. _meta.json is the only shared mutable state. Every write to it is built on a copy read from Drive moments earlier, merges into that copy instead of replacing it, and is read back afterwards to confirm it survived. A writer also remembers the change logs it wrote and restores any that go missing on its next load.
  • Compaction deletes nothing until its metadata write is confirmed. Until then, the change logs are still the only copy of the changes in them.
  • Sequence numbers come from Drive, not from a local counter, so two clients writing minutes apart cannot mint the same one.
  • A writer catches up before it writes, so it sees what its peers have appended. A client that only reads needs pollingIntervalMs to notice anything.

Caveats worth knowing before you fan out:

  • There is no compare-and-swap. Drive API v3 dropped ETags, so If-Match is accepted and ignored - the adapter still sends it (the emulated test server does enforce it), but nothing may assume it was honoured. Two clients writing metadata within the same round trip can still produce a lost update; the read-back and the restore-on-load above are what repair it, so a client that writes and immediately disconnects forever is the one case that can leave a change log unreferenced.
  • Pass folderId, not just folderName, when several clients may start at once. Duplicate _meta.json files are detected and resolved; duplicate folders are not, and two clients that each create "my-app-db-folder" get two databases.
  • A document written concurrently by two clients resolves last-writer-wins in the index, not through pouchdb-merge. Both revisions stay on Drive, but the losing one will not show up as a conflict branch.

Architecture

The adapter implements a "Remote-First" architecture:

  • Lazy Loading: db.get(id) fetches data on-demand from Drive.
  • Caching: Changes are indexed locally but bodies are cached in an LRU cache.
  • Resilience: Writes use optimistic locking to handle multi-client concurrency safer.

Documentation

  • Sync to Google Drive — registering the adapter, OAuth scope, one folder per database, restoring on a new device
  • Sync to a remote — the replication lifecycle this adapter plugs into
  • DocStack — the document data layer it was built for

License

CC-BY-SA-4.0