gdrive-db
v0.1.4
Published
A beginner-friendly database-like storage SDK powered by the end user's own Google Drive. Not a replacement for MongoDB/PostgreSQL/Firebase — built for learning, prototypes, demos, and small frontend projects.
Maintainers
Readme
gdrive-db
A beginner-friendly, database-like storage SDK powered by the end user's own Google Drive.
import { DriveDB, BrowserAuthProvider } from "gdrive-db";
const auth = new BrowserAuthProvider({
clientId: "YOUR_GOOGLE_OAUTH_CLIENT_ID",
});
const db = await DriveDB.connect({ database: "my-shop", auth });
const users = db.collection<{ name: string; email: string; age: number }>(
"users",
);
const user = await users.insert({
name: "Ali",
email: "[email protected]",
age: 19,
});
const allUsers = await users.find();
const ali = await users.findOne({ email: "[email protected]" });
await users.update(user.id, { age: 20 });
await users.delete(user.id);Simple Guide (Roman Urdu / Hindi)
Agar aap beginner hain aur simple lafzon mein samajhna chahte hain ke ye package frontend mein kaise connect hota hai, ye guide aapke liye hai.
Step 1: Google Cloud setup (ek baar karna hai)
- Google Cloud Console par jaake ek project banayein.
- Google Drive API enable karein (APIs & Services -> Library -> "Google Drive API" -> Enable).
- OAuth consent screen configure karein — personal project ke liye "External" + apne aap ko test user mein add karein.
- Credentials -> Create Credentials -> OAuth client ID -> Web application choose karein, aur
apne app ka origin (jaise
http://localhost:5173yahttp://localhost:3000) "Authorized JavaScript origins" mein add karein. - Yahan se aapko ek Client ID milega (kuch aisa:
xxxx.apps.googleusercontent.com) — ye public hai, browser code mein rakh sakte hain.
Step 2: Package install karein
npm install gdrive-dbStep 3: Frontend code mein connect karein
import { DriveDB, BrowserAuthProvider } from "gdrive-db";
const auth = new BrowserAuthProvider({
clientId: "YOUR_GOOGLE_OAUTH_CLIENT_ID",
});
const db = await DriveDB.connect({ database: "my-shop", auth });
const users = db.collection<{ name: string; email: string; age: number }>(
"users",
);
// Insert
const user = await users.insert({
name: "Ali",
email: "[email protected]",
age: 19,
});
// Read
const allUsers = await users.find();
// Update
await users.update(user.id, { age: 20 });
// Delete
await users.delete(user.id);Ye kaam kaise karta hai
- Jaise hi pehla Drive operation call hota hai, Google ka sign-in popup khulega — user apne Google account se login karega.
- Data user ke apne Google Drive mein
DataLens/my-shop/folder ke andar JSON files (users.jsonwaghera) ki shakal mein save hota hai. - Token sirf memory mein rehta hai (page reload par dobara login karna padega), koi server backend ki zaroorat nahi.
- Sirf
drive.filescope use hota hai — matlab app sirf apni banai hui files access karta hai, poori Drive nahi.
Important baatein
- Ye ek per-user storage hai — sab users ka data ek jagah share nahi hota, har user ka apna Drive.
- Ye production-grade database nahi hai (koi transactions/concurrency control nahi) — beginner projects, prototypes, demos ke liye best hai.
- Agar server-side (Node) use karna hai to
gdrive-db/nodeseNodeAuthProvideruse karein — lekin uske liye client secret aur refresh token chahiye hote hain jo kabhi bhi frontend code mein nahi jaane chahiye.
What it is
gdrive-db gives your app a small collection/document API — insert, find, findOne,
update, delete — while the actual data lives as JSON files inside a DataLens folder in the
signed-in user's own Google Drive. There is no shared backend, no database you have to host,
and no bill: each user's data lives in their own Drive, under their own quota.
Why it exists
Beginners building a first app, a class project, or a weekend prototype often need "somewhere to put the data" without standing up Postgres, Mongo, or Firebase and wiring up hosting, billing, and security rules. This package trades scale and guarantees for near-zero setup: sign in with Google, and you have a working per-user data store.
Who should use it
- Beginner developers and students learning to build apps with persistent data
- Small frontend projects, demos, and hackathon prototypes
- Hobby projects where "my own Google Drive" is an acceptable place to keep data
Who should NOT use it
- Anyone needing a shared/multi-user backend. Every collection lives in one Google account's Drive; there is no built-in way for multiple users to read/write the same dataset.
- Anyone needing production guarantees. No transactions, no concurrency control, no SLA, no indexing, no horizontal scale. See Limitations.
- Anyone who needs this to just work without the user signing in to Google. There is no "headless"/anonymous mode for end users — see Authentication.
This is not a free MongoDB, Postgres, or Firebase replacement. Think of it as: use your own Google Drive as a simple database backend for small frontend projects and prototypes.
Architecture
Developer Application
|
v
gdrive-db
|
v
Google OAuth (Identity Services, browser) / refresh-token OAuth (Node, server)
|
v
Google Drive REST API
|
v
User's own Google Drive
|
v
DataLens/<database>/
├── users.json
├── products.json
└── orders.jsonInternally the package is layered so each piece can be tested and reasoned about independently:
- auth — obtains a live Drive access token (
TokenProviderinterface). Two interchangeable implementations:BrowserAuthProvider(browser) andNodeAuthProvider(server, separate entry). - drive —
DriveClient, a thinfetch-based wrapper over the Drive REST v3 API (find/create folders and files, read/update/delete file content). Internal only. - storage —
CollectionStore, a JSON-file-per-collection abstraction on top ofDriveClient. - database —
DriveDB(connect,createCollection,listCollections,collection()) andCollectionImpl(the actual CRUD methods you call).
Installation
npm install gdrive-dbFor server-side/Node usage of NodeAuthProvider, also install the optional peer dependency:
npm install googleapisAuthentication
This is the part beginners get wrong most often when reaching for Drive as a backend, so it's worth being explicit: there is no way to talk to a user's private Google Drive without that user signing in with Google. This package never asks for more than that.
Browser (the primary, recommended path)
BrowserAuthProvider uses Google Identity Services
(GIS) — the current Google-recommended way to get an OAuth access token directly in a browser. It
needs only a public OAuth Client ID, never a client secret:
import { BrowserAuthProvider, DriveDB } from "gdrive-db";
const auth = new BrowserAuthProvider({
clientId: "xxxx.apps.googleusercontent.com",
});
const db = await DriveDB.connect({ database: "my-shop", auth });- The first Drive operation triggers Google's sign-in/consent popup.
- Access tokens are cached in memory only for the lifetime of the page — never written to
localStorage,sessionStorage,IndexedDB, or cookies. Reloading the page requires signing in again. Callauth.signOut()to discard the cached token explicitly. - No server component is required for this flow.
Server / Node (optional, advanced)
For a backend service that needs to act on Drive without a user present in a browser,
NodeAuthProvider (from the separate gdrive-db/node entry) uses a long-lived Google
OAuth refresh token:
import { NodeAuthProvider } from "gdrive-db/node";
import { DriveDB } from "gdrive-db";
const auth = new NodeAuthProvider({
clientId: process.env.GOOGLE_OAUTH_CLIENT_ID!,
clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET!,
refreshToken: process.env.GOOGLE_OAUTH_REFRESH_TOKEN!,
});
const db = await DriveDB.connect({ database: "my-shop", auth });This entry point is kept completely separate from the main package export (a different file,
marked external in the build) so bundlers building browser code never see a reference to
googleapis, and a client secret can never accidentally end up in a browser bundle. You obtain the
refresh token yourself, once, through your own server-side OAuth consent flow — this package does
not provide that flow, since it's inherently specific to your backend.
Never put clientSecret or refreshToken in browser code, in a NEXT_PUBLIC_*/VITE_*
variable, or anywhere a client can read it.
Google Cloud setup
- Go to the Google Cloud Console and create (or select) a project.
- Enable the Google Drive API for that project (APIs & Services -> Library -> "Google Drive API" -> Enable).
- Configure the OAuth consent screen (APIs & Services -> OAuth consent screen). For personal projects, "External" + test users is usually the right choice while developing.
- Create credentials (APIs & Services -> Credentials -> Create Credentials -> OAuth client ID):
- For browser usage: Web application, with your app's origin(s) under "Authorized JavaScript
origins". This gives you the public Client ID used by
BrowserAuthProvider. - For server usage only: create a client ID/secret and obtain a refresh token yourself via a one-time OAuth consent flow you run on your server.
- For browser usage: Web application, with your app's origin(s) under "Authorized JavaScript
origins". This gives you the public Client ID used by
- Copy
.env.exampleto.envand fill in your own values. Never commit.env.
Required scope
The package requests only:
https://www.googleapis.com/auth/drive.fileThis is Google's least-privilege Drive scope: it grants access only to files/folders the app
itself creates, not the user's entire Drive. gdrive-db never asks for broader access
than this.
Environment configuration
See .env.example. GOOGLE_OAUTH_CLIENT_ID is public and safe to ship in
browser code. GOOGLE_OAUTH_CLIENT_SECRET and GOOGLE_OAUTH_REFRESH_TOKEN are server-only secrets
— only needed for NodeAuthProvider, and must never reach browser code or version control.
Usage
import { DriveDB, BrowserAuthProvider } from "gdrive-db";
interface User {
name: string;
email: string;
age: number;
}
const auth = new BrowserAuthProvider({ clientId: "YOUR_CLIENT_ID" });
const db = await DriveDB.connect({ database: "my-shop", auth });
const users = db.collection<User>("users");CRUD examples
// Create
const user = await users.insert({
name: "Ali",
email: "[email protected]",
age: 19,
});
const many = await users.insertMany([
{ name: "Ahmed", email: "[email protected]", age: 22 },
{ name: "Sara", email: "[email protected]", age: 25 },
]);
// Read
const all = await users.find();
const nineteen = await users.find({ age: 19 });
const ali = await users.findOne({ email: "[email protected]" });
const byId = await users.findById(user.id);
const total = await users.count();
const isThere = await users.exists(user.id);
// Update (partial — other fields are left untouched)
await users.update(user.id, { age: 20 });
// Delete
await users.delete(user.id);
// Collections
await db.createCollection("orders");
const names = await db.listCollections(); // ["users", "orders"]
await users.drop(); // deletes users.json entirelyCollection API
| Method | Description |
| --------------------- | --------------------------------------------------------------- |
| insert(doc) | Insert one document, returns it with a generated id. |
| insertMany(docs) | Insert multiple documents at once. |
| find(filter?) | Return matching documents (equality filter, or all if omitted). |
| findOne(filter?) | Return the first match, or null. |
| findById(id) | Return the document with this id, or null. |
| update(id, updates) | Partially update a document by id. Throws if not found. |
| delete(id) | Remove a document by id. Returns true/false. |
| count(filter?) | Count matching documents. |
| all() | Return every document in the collection. |
| exists(id) | Whether a document with this id exists. |
| drop() | Delete the collection's JSON file from Drive entirely. |
Documents may contain strings, numbers, booleans, null, arrays, and nested objects — anything
JSON.stringify can represent. A document must not include its own id field; ids are always
generated by the package.
Limitations
- MVP filters are equality-only.
find({ age: 19 })matches documents whereage === 19. There is no$gt/$lt/$in/etc. yet (see Future roadmap). - No pagination.
find()/all()load the entire collection file into memory every time. - No indexing. Every read scans the full collection.
- Nested data works, but stays JSON-only. No binary data, dates are plain strings/numbers you
serialize yourself, no
Map/Set/custom classes.
Security
- Data lives inside the signed-in user's own Google Drive, under
DataLens/<database>/. It is never made public and never shared via "anyone with the link" — every request is authenticated per-user through OAuth. - The package requests the least-privilege
drive.filescope: only files/folders it creates itself, not the user's whole Drive. - This package never sees or handles the user's Google password — only an OAuth access token, obtained through Google's own sign-in UI.
- Browser access tokens are kept in memory only; nothing is written to
localStorageor similar. - A Node client secret / refresh token, if you use
NodeAuthProvider, is your responsibility to store securely (environment variables, a secrets manager) — this package never logs or persists them. - This package cannot make claims about the security of your Google account itself. You remain responsible for your own Google account security (strong password, 2FA, reviewing connected apps). Nothing here is "100% secure" or "completely private" — it inherits whatever security posture your Google account and network have.
Concurrency
Google Drive is not a transactional database. Every write reads the whole collection file, applies the change, and writes the whole file back. If two writers touch the same collection at nearly the same time, the second write can silently overwrite the first (a read-modify-write race) — there is no locking, no conflict detection, and no ACID guarantee in this MVP. This package is intended for small, low-concurrency projects: one user, or a small number of collaborators who aren't writing to the same collection at the same instant. Do not use it for anything where lost writes are unacceptable.
Storage & quota considerations
Even though Google Drive accounts often have generous storage quotas, this package is still constrained by:
- Browser/Node memory (a whole collection is loaded into memory per operation)
- Google Drive API rate limits and quotas
- Network speed (every read/write is a round trip to Drive)
- JSON file size (very large collections get slow to read/write, not just to store)
For the MVP, each collection is one JSON file. Sharding/chunking large collections is a possible future improvement, not implemented now.
Development
npm install
npm run dev # tsup --watch
npm run build # ESM + CJS + .d.ts output in dist/
npm run typecheckTesting
npm test # vitest run
npm run test:watchTests never make real Google API calls. DriveClient is tested against an injected fetch mock;
higher layers (CollectionStore, CollectionImpl, DriveDB) are tested against a shared
in-memory FakeDriveClient test double, so the whole connect → CRUD flow is exercised without any
network access.
Build
npm run buildProduces dist/index.{js,mjs,d.ts} (main, browser-safe entry) and dist/node.{js,mjs,d.ts}
(server-only entry, gdrive-db/node).
Publishing
Not published yet. Before publishing, verify the package contents:
npm pack --dry-runConfirm the tarball contains only dist/, README.md, and LICENSE — no test files, no .env,
no remaining-tasks.md, no source .ts files.
Future roadmap
Not implemented yet, listed here as ideas for later versions:
- Pagination, sorting, and advanced filter operators (
$gt,$lt,$in, etc.) - Batch writes and write-conflict detection
- File chunking/sharding for large collections
- Compression and client-side encryption
- Realtime sync and offline mode
- Alternate backends (Google Sheets, local SQLite, Supabase, Firebase)
- Migration tools, admin UI
Author
Built by Syed Muhammad Ali.
License
MIT — see LICENSE.
