Google Drive

A lightweight, dependency-free, in-memory fake of Google Drive API v3 for testing googleapis clients with zero external side effects.

Default port: 4614

Quick Start

import { google } from "googleapis";
import { GoogleDriveServer } from "./services/google-drive/src/server.js";

const server = new GoogleDriveServer(4614);
await server.start();

const drive = google.drive({ version: "v3", auth: "test" });
drive.context._options.rootUrl = "http://127.0.0.1:4614/";

const created = await drive.files.create({
  requestBody: { name: "notes.txt", mimeType: "text/plain" },
  media: { mimeType: "text/plain", body: "hello" },
});

await server.stop();

Reset state with server.reset() or POST /_parlel/reset.

Implemented Operations

Server

OperationEndpoint
Discovery markerGET /, GET /drive/v3, GET /v3
HealthcheckGET /_parlel/health
Reset ephemeral statePOST /_parlel/reset

About and Apps

googleapis methodEndpoint
drive.about.getGET /drive/v3/about
drive.apps.listGET /drive/v3/apps
drive.apps.getGET /drive/v3/apps/{appId}

Files

googleapis methodEndpoint
drive.files.createPOST /drive/v3/files, `POST /upload/drive/v3/files?uploadType=media
drive.files.getGET /drive/v3/files/{fileId}
drive.files.get mediaGET /drive/v3/files/{fileId}?alt=media
drive.files.listGET /drive/v3/files
drive.files.updatePATCH /drive/v3/files/{fileId}, `PATCH /upload/drive/v3/files/{fileId}?uploadType=media
drive.files.deleteDELETE /drive/v3/files/{fileId}
drive.files.copyPOST /drive/v3/files/{fileId}/copy
drive.files.exportGET /drive/v3/files/{fileId}/export
drive.files.downloadPOST /drive/v3/files/{fileId}/download
drive.files.emptyTrashDELETE /drive/v3/files/trash
drive.files.generateCseTokenGET /drive/v3/files/generateCseToken
drive.files.generateIdsGET /drive/v3/files/generateIds
drive.files.watchPOST /drive/v3/files/{fileId}/watch
drive.files.listLabelsGET /drive/v3/files/{fileId}/listLabels
drive.files.modifyLabelsPOST /drive/v3/files/{fileId}/modifyLabels

Supported file query clauses: trashed = true|false, starred = true|false, mimeType = '...', name = '...', name contains '...', 'parentId' in parents, and fullText contains '...'.

Permissions

googleapis methodEndpoint
drive.permissions.createPOST /drive/v3/files/{fileId}/permissions
drive.permissions.listGET /drive/v3/files/{fileId}/permissions
drive.permissions.getGET /drive/v3/files/{fileId}/permissions/{permissionId}
drive.permissions.updatePATCH /drive/v3/files/{fileId}/permissions/{permissionId}
drive.permissions.deleteDELETE /drive/v3/files/{fileId}/permissions/{permissionId}

Revisions

googleapis methodEndpoint
drive.revisions.listGET /drive/v3/files/{fileId}/revisions
drive.revisions.getGET /drive/v3/files/{fileId}/revisions/{revisionId}
drive.revisions.updatePATCH /drive/v3/files/{fileId}/revisions/{revisionId}
drive.revisions.deleteDELETE /drive/v3/files/{fileId}/revisions/{revisionId}

Comments and Replies

googleapis methodEndpoint
drive.comments.createPOST /drive/v3/files/{fileId}/comments
drive.comments.listGET /drive/v3/files/{fileId}/comments
drive.comments.getGET /drive/v3/files/{fileId}/comments/{commentId}
drive.comments.updatePATCH /drive/v3/files/{fileId}/comments/{commentId}
drive.comments.deleteDELETE /drive/v3/files/{fileId}/comments/{commentId}
drive.replies.createPOST /drive/v3/files/{fileId}/comments/{commentId}/replies
drive.replies.listGET /drive/v3/files/{fileId}/comments/{commentId}/replies
drive.replies.getGET /drive/v3/files/{fileId}/comments/{commentId}/replies/{replyId}
drive.replies.updatePATCH /drive/v3/files/{fileId}/comments/{commentId}/replies/{replyId}
drive.replies.deleteDELETE /drive/v3/files/{fileId}/comments/{commentId}/replies/{replyId}

Shared Drives

googleapis methodEndpoint
drive.drives.createPOST /drive/v3/drives?requestId=...
drive.drives.listGET /drive/v3/drives
drive.drives.getGET /drive/v3/drives/{driveId}
drive.drives.updatePATCH /drive/v3/drives/{driveId}
drive.drives.deleteDELETE /drive/v3/drives/{driveId}
drive.drives.hidePOST /drive/v3/drives/{driveId}/hide
drive.drives.unhidePOST /drive/v3/drives/{driveId}/unhide

Legacy Team Drives

googleapis methodEndpoint
drive.teamdrives.createPOST /drive/v3/teamdrives?requestId=...
drive.teamdrives.listGET /drive/v3/teamdrives
drive.teamdrives.getGET /drive/v3/teamdrives/{teamDriveId}
drive.teamdrives.updatePATCH /drive/v3/teamdrives/{teamDriveId}
drive.teamdrives.deleteDELETE /drive/v3/teamdrives/{teamDriveId}

Changes and Channels

googleapis methodEndpoint
drive.changes.getStartPageTokenGET /drive/v3/changes/startPageToken
drive.changes.listGET /drive/v3/changes?pageToken=...
drive.changes.watchPOST /drive/v3/changes/watch
drive.channels.stopPOST /drive/v3/channels/stop

Operations

googleapis methodEndpoint
drive.operations.getGET /drive/v3/operations/{name}

files.download returns a completed operation object that can be fetched with operations.get.

Approvals

googleapis methodEndpoint
drive.approvals.startPOST /drive/v3/files/{fileId}/approvals:start
drive.approvals.listGET /drive/v3/files/{fileId}/approvals
drive.approvals.getGET /drive/v3/files/{fileId}/approvals/{approvalId}
drive.approvals.approvePOST /drive/v3/files/{fileId}/approvals/{approvalId}:approve
drive.approvals.declinePOST /drive/v3/files/{fileId}/approvals/{approvalId}:decline
drive.approvals.cancelPOST /drive/v3/files/{fileId}/approvals/{approvalId}:cancel
drive.approvals.commentPOST /drive/v3/files/{fileId}/approvals/{approvalId}:comment
drive.approvals.reassignPOST /drive/v3/files/{fileId}/approvals/{approvalId}:reassign

Access Proposals

googleapis methodEndpoint
drive.accessproposals.listGET /drive/v3/files/{fileId}/accessproposals
drive.accessproposals.getGET /drive/v3/files/{fileId}/accessproposals/{proposalId}
drive.accessproposals.resolvePOST /drive/v3/files/{fileId}/accessproposals/{proposalId}:resolve

Access proposals are read/resolve-only in the public Drive API, so tests may seed them directly in memory.

Surface coverage

This emulator faithfully replicates the API surface most application code and agents exercise. Anything below the supported lines is either an intentional design choice for a fast, zero-cost local emulator (✓ By design) or a candidate for a future release (⟳ Roadmap) — never a silent inaccuracy.

Legend: ✅ fully supported · ◐ accepted (stored, not strictly enforced) · ✓ by design · ⟳ on the roadmap.

FeatureStatusNotes
In-memory files, folders, metadata, parents, trash, star stateSupportedState is ephemeral and resettable.
JSON metadata create/updateSupportedField masks are accepted but not interpreted.
Media and multipart uploadSupporteduploadType=media and uploadType=multipart are implemented.
Media download and exportSupportedReturns stored bytes with requested content type for export.
File list pagingSupportedUses numeric pageToken offsets.
Common Drive q filtersSupportedSee Files section for supported clauses.
PermissionsSupportedNo real ACL enforcement.
Comments and repliesSupportedDelete marks items deleted like the real API.
RevisionsSupportedCreated on media/metadata update. The only revision cannot be deleted.
Shared drivesSupportedMetadata lifecycle only.
Legacy team drivesSupportedAliased to the same in-memory shared drive store.
Watches/channelsSupportedStored in memory; no outbound webhook delivery.
Access proposalsSupportedList/get/resolve only, matching the public API surface.
ApprovalsSupportedIn-memory approval state transitions only.
Long-running operationsSupportedOperations complete immediately.
OAuth, auth scopes, quota enforcementIntentionally unsupportedThe fake trusts all requests.
Resumable uploadsIntentionally unsupportedUse media or multipart uploads for tests.
Google Docs native conversion fidelityIntentionally unsupportedExport returns stored bytes.
Complex search grammar and fields projectionIntentionally unsupportedUnknown query clauses are treated as matches; responses are full resources.

Error Shape

Errors use Google JSON error framing:

{
  "error": {
    "code": 404,
    "message": "File not found",
    "status": "NOT_FOUND",
    "errors": [
      { "message": "File not found", "domain": "global", "reason": "notFound" }
    ]
  }
}

Common returned reasons include notFound, invalidArgument, parseError, alreadyExists, and methodNotAllowed.

<!-- parlel:testenv:start -->

Configuration — test.env

Copy these into your test.env (used by the bridge sidecar flow). Tokens are Parlel's seeded test credentials — any non-empty value is accepted by the emulator, so you rarely need to change them. Swap in real credentials only when pointing at the live service in prod.env.

GOOGLE_DRIVE_EMULATOR_HOST=http://parlel-bridge:4614
GOOGLE_CLOUD_PROJECT=parlel
GCLOUD_PROJECT=parlel
<!-- parlel:testenv:end -->