@beignet/provider-storage-vercel-blob
v0.0.40
Published
Vercel Blob object storage provider for Beignet
Maintainers
Readme
@beignet/provider-storage-vercel-blob
Vercel Blob object storage provider for Beignet
[!CAUTION] Beignet is experimental alpha software. The
0.0.xpackage line is for early evaluation, and APIs may change between releases while the framework settles.
This package adapts Vercel Blob to
Beignet's StoragePort, so apps deployed on Vercel get durable object storage
without hand-writing an adapter.
createVercelBlobStorageProvider(...) returns the stable
VercelBlobStorageProvider type. VercelBlobConfig describes its validated
config; the Zod schema remains internal.
Install
bun add @beignet/provider-storage-vercel-blob @vercel/blobOr scaffold everything — dependencies, provider wiring, ports, env example, and setup notes — with the CLI preset:
bun beignet provider add storage-vercel-blobProvider setup
// server/providers.ts
import { createVercelBlobStorageProvider } from "@beignet/provider-storage-vercel-blob";
export const providers = [
// ...
createVercelBlobStorageProvider(),
] as const;The provider contributes ports.storage (the standard Beignet StoragePort)
and ports.vercelBlob (an escape hatch, see below). Configuration comes from
BLOB_* env vars:
| Env var | Meaning |
| --- | --- |
| BLOB_READ_WRITE_TOKEN | Read-write token. Set automatically on Vercel deployments with a connected Blob store; set it manually elsewhere. |
| BLOB_ACCESS | Store-wide access level: private (default) or public. |
| BLOB_KEY_PREFIX | Optional prefix applied to every object key. |
| BLOB_STORE_ID | Optional store id when authenticating with a Vercel OIDC token. |
Local storage keeps working with zero setup by switching on the token, the same way the environment-swapped rate-limit providers work:
import { createLocalStorageProvider } from "@beignet/provider-storage-local";
import { createVercelBlobStorageProvider } from "@beignet/provider-storage-vercel-blob";
export const providers = [
// Vercel Blob in deployed environments (local disk does not survive
// serverless); the local provider keeps dev working with zero setup.
process.env.BLOB_READ_WRITE_TOKEN
? createVercelBlobStorageProvider()
: createLocalStorageProvider(),
] as const;Visibility
Vercel Blob stores per-object access at write time but does not report it
back from head(...), so this adapter keeps the whole store uniform: every
object shares the configured access level, and a put(...) that requests a
different visibility is rejected with an error instead of silently
misreporting visibility on later reads. Run two providers over two Blob
stores when an app needs both levels.
publicUrl(...) returns the blob URL for public stores and null for
private stores. Private objects should be served through app routes that
enforce the app's own authorization, then stream the body from
ctx.ports.storage.get(...).
Vercel Blob has no arbitrary per-object metadata, so StorageObject.metadata
is always reported empty. Cache lifetimes map from the Cache-Control
max-age directive to Vercel Blob's cacheControlMaxAge seconds; other
directives do not survive the mapping.
Direct port factory
Use the direct factory when you own provider wiring or need a second store:
import { createVercelBlobStorage } from "@beignet/provider-storage-vercel-blob";
const storage = createVercelBlobStorage({
access: "public",
keyPrefix: "avatars",
token: process.env.AVATARS_BLOB_TOKEN,
});Omit token to let @vercel/blob read BLOB_READ_WRITE_TOKEN from the
environment. The client option accepts a fake SDK surface for tests.
Request cost
Some StoragePort promises need an extra Blob API round-trip:
put(...)issues a post-writehead(...)because the port returns the authoritative stored object, not an echo of inputs.delete(...)issues a pre-deletehead(...)because Vercel Blob'sdel(...)does not report whether the object existed.
Escape hatch
ports.vercelBlob exposes the raw pieces for integrations the port does not
cover:
const { client, access, keyPrefix, objectKey, checkHealth } = ctx.ports.vercelBlob;
await client.list({ prefix: objectKey("exports"), limit: 100 });
const health = await checkHealth();checkHealth() issues a one-item list(...) and reports { ok, message?,
metadata } for readiness endpoints.
Devtools
When @beignet/devtools is installed before this provider, storage
operations appear under the dashboard's Storage watcher. The provider records
storage.put, storage.get, storage.stat, storage.delete,
storage.exists, and storage.publicUrl events with key, size, and
duration; failures are recorded with .failed event names and the original
error is rethrown.
Failure behavior
Missing objects resolve to null (get, stat, publicUrl) or false
(delete, exists) — BlobNotFoundError never escapes the port. All other
SDK errors are rethrown unchanged after an instrumentation event, including
missing-token errors, so misconfiguration fails loudly rather than
pretending storage is empty.
Direct uploads
This package covers server-mediated storage through StoragePort. Vercel
Blob's browser-direct uploads use its own client-token protocol
(@vercel/blob/client), which does not fit Beignet's plain presigned-PUT
UploadSignerPort; use createS3UploadSigner from
@beignet/provider-storage-s3 when direct uploads are a requirement, or
Vercel's client SDK directly.
Local and tests
Keep dev and tests on @beignet/provider-storage-local or
createMemoryStorage() from @beignet/core/ports; both implement the same
port. Inject a fake VercelBlobClient through the client/createClient
options to test app code against this adapter without network access.
Deployment notes
Connect a Blob store to the Vercel project so BLOB_READ_WRITE_TOKEN is
provisioned automatically, and prefer private stores unless objects are
genuinely world-readable: public blob URLs are unauthenticated and
long-cached.
