Package reference
Mirrors the package README (single source). Install @basaltkit/storage-gcs v1.3.2 — npm · source.
<p align="center"> <a href="https://basaltkit-docs.pages.dev"> <img src="https://basaltkit-docs.pages.dev/social-card.png" alt="Basalt" width="440"> </a> </p>
@basaltkit/storage-gcs
Google Cloud Storage driver for @basaltkit/storage: stores files on GCS without changing your app's code. You need this module when you run on Google Cloud and want GCS instead of S3 or local disk.
Installation
pnpm add @basaltkit/storage-gcs @google-cloud/storage@google-cloud/storage is a peer dependency. Credentials follow GCP's standard chain (ADC, keyFilename, service account).
Usage
import { storagePlugin } from '@basaltkit/storage'
import { GcsStorageDriver } from '@basaltkit/storage-gcs'
storagePlugin({
disks: { uploads: { driver: new GcsStorageDriver({ bucket: 'my-bucket', projectId: 'my-project' }) } },
})Implements the StorageDriver contract — put, get, exists, delete, list, and signed URLs (temporaryUrl). Per-tenant isolation, key validation and the opt-in upload limits all happen in Disk above this driver, so they apply here unchanged.
Options reference
new GcsStorageDriver(options: GcsDriverOptions):
| Option | Type | Default | Purpose |
|---|---|---|---|
bucket | string | — (required) | Bucket the disk maps to. |
projectId | string | from the ambient GCP credentials | Pin the project explicitly instead of inheriting it from ADC. |
keyFilename | string | ADC chain | Path to a service-account JSON key. Omit it on GCE/GKE/Cloud Run and let Application Default Credentials work. |
client | GcsBucketLike | — | Pre-built bucket handle. Bypasses projectId/keyFilename and the dynamic @google-cloud/storage import — used by tests, or to reuse a client you already authenticate yourself. |
The @google-cloud/storage module is imported lazily, on the first operation, so installing this package without using it costs nothing at boot.
Signed URLs and content disposition
temporaryUrl signs a read URL and pins the response disposition into the signature (responseDisposition). It defaults to attachment — matching the Disk default — so an uploaded HTML or SVG object downloads instead of rendering top-level on the storage origin. Pass { disposition: 'inline' } through disk.temporaryUrl(path, expiresIn, options) when in-browser rendering is deliberate.
Direct browser uploads
temporaryUploadUrl (via disk.temporaryUploadUrl(...)) signs a V4 write URL. The Content-Type is signed, and a declared contentLength is signed as x-goog-content-length-range: n,n, so GCS rejects any other type or size. Send the returned headers verbatim. checksumSha256 is refused with STORAGE_UPLOAD_URL_UNSUPPORTED (GCS verifies MD5/CRC32C only). A custom client fake receives the config typed as GcsSignedUrlConfig.
delete() checks exists() first so it can return false for a missing object rather than throwing — that costs one extra round-trip per delete.
Streaming, copy and stat
All four optional capabilities are implemented:
| Capability | GCS call | Notes |
|---|---|---|
putStream | createWriteStream | A resumable upload GCS chunks itself, in bounded memory, so a body of unknown length streams fine — no contentLength needed. A declared one is verified by the Disk; the object is finalized only when the body ends cleanly. |
getStream | createReadStream | Returns a Node Readable; consume it or destroy() it. GCS only discovers a missing object once the download starts, so STORAGE_FILE_NOT_FOUND arrives as an error on the stream, not as a rejected promise. |
copy | file.copy() | GCS rewrites the object server-side; the bytes never reach the process. |
stat | getMetadata() | { size, contentType, etag, lastModified } (GCS reports size as a string; it is coerced). |
An injected fake client that omits one of these methods makes that capability report STORAGE_*_UNSUPPORTED instead of crashing.
Endpoint overrides are refused. V4 signatures are bound to the bucket host and file.getSignedUrl takes no endpoint, so { endpoint } on temporaryUrl / temporaryUploadUrl throws STORAGE_TEMPORARY_URL_UNSUPPORTED / STORAGE_UPLOAD_URL_UNSUPPORTED rather than minting a URL for the wrong host. Use a bucket-bound hostname (cname) on the @google-cloud/storage client.
Errors
| Error | Code | HTTP | When |
|---|---|---|---|
StorageFileNotFoundError | STORAGE_FILE_NOT_FOUND | 500 | get() on an object that doesn't exist (GCS error code: 404). Re-exported from @basaltkit/storage. |
Any other GCS client error propagates unchanged. This driver defines no error classes of its own; the facade-level errors (STORAGE_INVALID_KEY, STORAGE_TOO_LARGE, STORAGE_CONTENT_TYPE, STORAGE_TEMPORARY_URL_UNSUPPORTED) are raised by Disk before the driver is reached. Storage errors carry no HTTP status, so the adapters surface them as a generic 500 INTERNAL_ERROR — catch and map them in your handler.
Hooks & events
None. Upload lifecycle events live in @basaltkit/files.
Testable without the cloud
The client (bucket) is injectable, so the driver's logic can be tested with a fake — no GCS required:
new GcsStorageDriver({ bucket: 'b', client: fakeBucket })How it connects to other modules
@basaltkit/storage— this is a driver for that package; the API (Disk,storagePlugin) comes from there.- Sibling drivers:
S3StorageDriver(in core) and@basaltkit/storage-azure.
Guide: Storage.