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

gdrive-storage

v0.1.0

Published

A beginner-friendly file/media storage SDK powered by the end user's own Google Drive. Sister package to gdrive-db — free image/video storage with no backend to host and no bill.

Readme

gdrive-storage

npm version npm downloads GitHub stars License: MIT Build Tests TypeScript

A beginner-friendly file/media storage SDK powered by the end user's own Google Drive.

import { DriveStorage, BrowserAuthProvider } from "gdrive-storage";

const auth = new BrowserAuthProvider({
  clientId: "YOUR_GOOGLE_OAUTH_CLIENT_ID",
});
const storage = await DriveStorage.connect({ bucket: "my-app-media", auth });

const photo = await storage.upload(fileInput.files[0]);
const allFiles = await storage.list();
const bytes = await storage.download(photo.id);
const objectUrl = await storage.getObjectUrl(photo.id); // drop straight into <img src>
await storage.delete(photo.id);

gdrive-storage is the sister package to gdrive-db — install both and sign in once with the same Google account to get "a free database" (gdrive-db) and "free image/video storage" (gdrive-storage) for a hobby project or prototype, with no backend to host and no bill.

What it is

gdrive-storage gives your app a small file storage API — upload, list, get, download, delete, exists, and a browser convenience getObjectUrl — while the actual files live inside a MediaVault folder in the signed-in user's own Google Drive. There is no shared backend, no storage bucket you have to host, and no bill: each user's files live 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 uploaded images/videos" without standing up S3, Cloudinary, or Firebase Storage and wiring up hosting, billing, and access rules. This package trades scale and guarantees for near-zero setup: sign in with Google, and you have a working per-user file store. Pair it with gdrive-db for a "free mini-backend" — JSON data plus file storage, both riding on the same Google sign-in.

Who should use it

  • Beginner developers and students learning to build apps that handle file/image/video uploads
  • Small frontend projects, demos, and hackathon prototypes
  • Hobby projects where "my own Google Drive" is an acceptable place to keep files

Who should NOT use it

  • Anyone needing a shared/multi-user backend. Every bucket lives in one Google account's Drive; there is no built-in way for multiple users to read/write the same files.
  • Anyone needing public/CDN-backed file hosting. There are no shareable links and no CDN — see Limitations.
  • Anyone needing image/video processing. No resizing, thumbnailing, or transcoding — store and retrieve the original bytes only.
  • 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 S3, Cloudinary, or Firebase Storage replacement. Think of it as: use your own Google Drive as simple file storage for small frontend projects and prototypes.

Architecture

Developer Application
        |
        v
gdrive-storage
        |
        v
Google OAuth (Identity Services, browser)  /  refresh-token OAuth (Node, server)
        |
        v
Google Drive REST API (multipart upload for small files, resumable upload for large files)
        |
        v
User's own Google Drive
        |
        v
MediaVault/<bucket>/
        ├── photo-abc123.jpg
        ├── clip-def456.mp4
        └── ...

Internally the package is layered so each piece can be tested and reasoned about independently:

  • auth — obtains a live Drive access token (TokenProvider interface). Two interchangeable implementations: BrowserAuthProvider (browser) and NodeAuthProvider (server, separate entry).
  • driveDriveClient, a thin fetch-based wrapper over the Drive REST v3 API: folder find/create, multipart upload (small files), resumable upload (large files), download, list, delete, and metadata lookup.
  • storageFileStore, the bucket-folder abstraction on top of DriveClient: upload routing (small vs. large), MIME-type and file-size validation.
  • top-level APIDriveStorage (connect, upload, list, get, download, exists, delete, getObjectUrl).

Installation

npm install gdrive-storage

For server-side/Node usage of NodeAuthProvider, also install the optional peer dependency:

npm install googleapis

Authentication

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, DriveStorage } from "gdrive-storage";

const auth = new BrowserAuthProvider({
  clientId: "xxxx.apps.googleusercontent.com",
});
const storage = await DriveStorage.connect({ bucket: "my-app-media", 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. Call auth.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-storage/node entry) uses a long-lived Google OAuth refresh token:

import { NodeAuthProvider } from "gdrive-storage/node";
import { DriveStorage } from "gdrive-storage";

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 storage = await DriveStorage.connect({ bucket: "my-app-media", 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

  1. Go to the Google Cloud Console and create (or select) a project.
  2. Enable the Google Drive API for that project (APIs & Services -> Library -> "Google Drive API" -> Enable).
  3. Configure the OAuth consent screen (APIs & Services -> OAuth consent screen). For personal projects, "External" + test users is usually the right choice while developing.
  4. 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.
  5. Copy .env.example to .env and fill in your own values. Never commit .env.

Required scope

The package requests only:

https://www.googleapis.com/auth/drive.file

This 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-storage 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.

Frontend usage (Browser)

This is the primary, recommended way to use gdrive-storage — no server component required. Each end user signs in with their own Google account and files are stored in their own Drive.

1. Install and get a Client ID

npm install gdrive-storage

Follow Google Cloud setup below once to get a public OAuth Client ID (looks like xxxx.apps.googleusercontent.com).

2. Connect

import { DriveStorage, BrowserAuthProvider } from "gdrive-storage";

const auth = new BrowserAuthProvider({
  clientId: "YOUR_GOOGLE_OAUTH_CLIENT_ID",
});

const storage = await DriveStorage.connect({ bucket: "my-app-media", auth });

Calling connect() (or any other method before it) triggers Google's sign-in/consent popup the first time. Behind the scenes it finds-or-creates a MediaVault/my-app-media/ folder in the signed-in user's Drive and scopes every subsequent call to that folder.

3. Upload a file picked with <input type="file">

<input id="fileInput" type="file" accept="image/*,video/*" />
const fileInput = document.getElementById("fileInput");

fileInput.addEventListener("change", async () => {
  const file = fileInput.files[0];
  if (!file) return;

  const uploaded = await storage.upload(file, {
    onProgress: (pct) => console.log(`Upload progress: ${pct}%`),
  });
  console.log(uploaded);
  // => { id, name, mimeType, size, createdTime, modifiedTime }
});

upload() reads the File's name and MIME type automatically — you don't need to pass them yourself unless you want to override them.

4. Show it with <img> / <video>

const objectUrl = await storage.getObjectUrl(uploaded.id);
document.getElementById("preview").src = objectUrl;

// When the element is removed / you no longer need the preview:
URL.revokeObjectURL(objectUrl);

5. List, check, and delete

const allFiles = await storage.list(); // metadata for every file in the bucket
const meta = await storage.get(uploaded.id); // metadata only, or null if missing
const isThere = await storage.exists(uploaded.id);

await storage.delete(uploaded.id);

6. Sign out

auth.signOut(); // discards the in-memory token; next call re-triggers sign-in

See examples/browser/index.html for a complete working page (upload input, live preview, file list with delete buttons).

Backend usage (Node / server)

Use this when a server process needs to read/write a Drive on its own behalf — for example a background job, an admin script, or a backend that manages one dedicated Google account's files. This is not the path for "each of my app's end users has their own Drive" — that's the frontend flow above.

1. Install

npm install gdrive-storage googleapis

googleapis is an optional peer dependency, only required for NodeAuthProvider.

2. Get a refresh token (one-time, done by you)

NodeAuthProvider needs a Client ID, Client Secret, and a long-lived refresh token for the Google account whose Drive you want to use. Obtain these once, yourself, through your own server-side OAuth consent flow — this package does not provide that flow, since the details depend on your backend/framework. In short:

  1. Create an OAuth Client ID of type Desktop app or Web application in the Google Cloud Console (see Google Cloud setup).
  2. Run a one-time OAuth consent flow (e.g. with the googleapis package's google.auth.OAuth2 client and access_type: "offline") to get a refresh token for the target account.
  3. Store the Client ID, Client Secret, and refresh token as server-only environment variables — never commit them, never expose them to browser code.

3. Connect and use

import { NodeAuthProvider } from "gdrive-storage/node";
import { DriveStorage } from "gdrive-storage";
import { readFile } from "node:fs/promises";

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 storage = await DriveStorage.connect({ bucket: "my-app-media", auth });

// Upload a Buffer read from disk, or any Uint8Array/ArrayBuffer you already have
const fileBuffer = await readFile("./clip.mp4");
const uploaded = await storage.upload(fileBuffer, {
  name: "clip.mp4",
  mimeType: "video/mp4",
});

// List / get / download / delete work exactly the same as in the browser
const allFiles = await storage.list();
const meta = await storage.get(uploaded.id);
const bytes = await storage.download(uploaded.id); // Uint8Array
await storage.delete(uploaded.id);

getObjectUrl() is browser-only (it relies on URL.createObjectURL) and throws DriveStorageError if called in Node — use download() and write the bytes to disk, a response stream, or wherever you need them instead:

import { writeFile } from "node:fs/promises";

const bytes = await storage.download(uploaded.id);
await writeFile("./downloaded-clip.mp4", bytes);

Never put clientSecret or refreshToken in browser code, in a NEXT_PUBLIC_*/VITE_* variable, or anywhere a client can read it. This entry point (gdrive-storage/node) is kept completely separate from the main package export precisely so bundlers building browser code never even see a reference to googleapis.

API reference

Applies the same way whether auth came from BrowserAuthProvider or NodeAuthProvider — the DriveStorage instance itself doesn't know or care which environment it's running in.

Bucket options

const storage = await DriveStorage.connect({
  bucket: "my-app-media",
  auth,
  acceptedTypes: ["image/*", "video/*"], // optional allow-list; open (any type) by default
  maxFileSize: 50 * 1024 * 1024, // optional, in bytes; defaults to 100MB
});

Methods

| Method | Description | | ------------------------ | ------------------------------------------------------------------- | | upload(data, options?) | Upload a File/Blob/Buffer/Uint8Array/ArrayBuffer, returns metadata. | | list() | Return metadata for every file in the bucket. | | get(id) | Return one file's metadata, or null if not found. | | download(id) | Return the file's raw bytes as a Uint8Array. Throws if not found. | | exists(id) | Whether a file with this id exists. | | delete(id) | Delete a file by id. Throws if not found. | | getObjectUrl(id) | Browser only: downloads and wraps the file as an object URL. |

upload() picks Drive's multipart upload for files at or under 5MB, and resumable upload above that — you don't need to choose.

Limitations

  • No public/shareable links. drive.file-scoped files are private to the app+user; there is no "anyone with the link can view" mode without a broader, more privacy-invasive OAuth scope this package deliberately does not request.
  • No image/video transformations. No resizing, thumbnailing, cropping, or transcoding — store and retrieve the original bytes only.
  • No CDN. Every read (download, getObjectUrl) is an authenticated round trip to Drive.
  • No dedup, no versioning, no subfolders. Flat namespace per bucket — uploading twice creates two separate files, and there is no folder nesting beyond the one bucket folder.
  • Upload progress is coarse in this MVPonProgress fires with 0 then 100; granular chunked-upload progress is a fast-follow (see Future roadmap).

Security

  • Files live inside the signed-in user's own Google Drive, under MediaVault/<bucket>/. They are 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.file scope: 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 localStorage or 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.

Storage & quota considerations

Even though Google Drive accounts often have generous storage quotas, this package is still constrained by:

  • Your Google account's Drive storage quota (uploaded files count against it like any other file)
  • Google Drive API rate limits and quotas
  • Network speed (every upload/download is a round trip to Drive)
  • Browser/Node memory (a whole file's bytes are held in memory for upload/download — there is no streaming in this MVP)

Development

npm install
npm run dev      # tsup --watch
npm run build     # ESM + CJS + .d.ts output in dist/
npm run typecheck

Testing

npm test          # vitest run
npm run test:watch

Tests never make real Google API calls. DriveClient is tested against an injected fetch mock; higher layers (FileStore, DriveStorage) are tested against a shared in-memory FakeDriveClient test double, so the whole connect → upload/list/download/delete flow is exercised without any network access.

Build

npm run build

Produces dist/index.{js,mjs,d.ts} (main, browser-safe entry) and dist/node.{js,mjs,d.ts} (server-only entry, gdrive-storage/node).

Publishing

Not published yet. Before publishing, verify the package contents:

npm pack --dry-run

Confirm 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:

  • Granular, chunked resumable-upload progress
  • Public/shareable link support behind an explicit, separately-documented opt-in
  • File dedup and versioning
  • Client-side image/video thumbnailing before upload
  • Streaming upload/download for very large files (avoid holding the whole file in memory)
  • Migration tools, admin UI

Author

Built by Syed Muhammad Ali.

License

MIT — see LICENSE.