@vite-hub/blob
v0.0.3
Published
Vite-first blob storage primitives and deployment integration for ViteHub.
Downloads
3,063
Maintainers
Readme
@vite-hub/blob
@vite-hub/blob gives server code one object-storage API across local files and hosted blob providers.
Install
pnpm add @vite-hub/blobAdd the SDK required by the driver you configure.
Minimal API
// server/api/files.post.ts
import { blob } from "@vite-hub/blob"
import { defineEventHandler, readBody } from "h3"
export default defineEventHandler(async (event) => {
const body = await readBody<{ path: string, text: string }>(event)
const [writeError] = await blob.put(body.path, body.text, { contentType: "text/plain" })
if (writeError) throw writeError
const [readError, file] = await blob.get(body.path)
if (readError) throw readError
return file
})// vite.config.ts
import { hubBlob } from "@vite-hub/blob/vite"
import { defineConfig } from "vite"
export default defineConfig({
blob: {
driver: "fs",
base: ".data/blob",
},
plugins: [hubBlob()],
})Vite Integration
Use hubBlob() in Vite to resolve blob config and expose the blob runtime helper to server code.
Core drivers include local fs, Vercel Blob, Cloudflare R2, S3-compatible stores, and files-sdk.
At config time, the fs driver uses BLOB_FS_BASE when blob.base is omitted, then defaults to .data/blob.
Set blob.serve to generate a Nitro route for serving Blob-backed assets. serve: true uses /api/_vitehub/blob as a safe namespaced API route. Use serve.route for product-facing paths such as /assets.
Objects from the served store receive an absolute URL when serve.publicBaseUrl is configured, or a route-relative URL otherwise.
Use serve.headers for static cache and security headers. Blob metadata remains authoritative for content headers such as Content-Type, Content-Length, and ETag.
Use detectContentType() when an application needs to classify leading bytes before storage. It returns a detected MIME type for common images and PDFs, or undefined when the signature is unknown. Storage contentType remains caller-provided metadata, and recognizing a signature does not prove that a complete file is valid or safe.
import { detectContentType } from "@vite-hub/blob/content-type"
const detected = detectContentType(new Uint8Array(await file.arrayBuffer()))
if (detected !== file.type) throw new Error("File content does not match its declared type")// vite.config.ts
export default defineConfig({
blob: {
driver: "fs",
serve: {
route: "/assets",
headers: {
"Cache-Control": "public, max-age=300",
"X-Content-Type-Options": "nosniff",
},
},
},
plugins: [hubBlob()],
})Blob stores binary objects and small object metadata. Keep catalogs, indexes, permissions, search records, domain records, and richer metadata queries in KV, Database, or another NoSQL/catalog store next to Blob.
Signed requests
Use blob.sign() to grant short-lived access to one private object without routing its body through your server.
const [downloadError, download] = await blob.sign("private/audio.mp3", {
method: "GET",
expiresIn: 60 * 60,
})
if (downloadError) throw downloadError
const [uploadError, upload] = await blob.sign("private/audio.mp3", {
method: "PUT",
expiresIn: 15 * 60,
contentType: "audio/mpeg",
createOnly: true,
})
if (uploadError) throw uploadError
await fetch(upload.url, {
method: upload.method,
headers: upload.headers,
body: file,
})Blob operations return [error, value]. Provider and storage failures use ViteHubError with a stable BLOB_* code, operation/store details, and the provider failure in cause. Invalid arguments, unknown stores, and unsupported signing capabilities still throw because they are configuration or API misuse rather than an operational result.
The returned headers are part of the request contract and must be sent unchanged. createOnly prevents overwriting an existing object when the driver can enforce a conditional upload.
S3-compatible storage
Use driver: "s3" for production S3-compatible object storage. Use driver: "minio" for local or Docker Compose object storage, and use driver: "cloudflare-r2" for Cloudflare R2.
pnpm add files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presignerCloudflare R2 HTTP fallback also requires:
pnpm add @aws-sdk/lib-storage// vite.config.ts
export default defineConfig({
blob: {
driver: "s3",
bucket: "app-assets",
endpoint: process.env.S3_ENDPOINT,
region: process.env.S3_REGION,
publicBaseUrl: "https://assets.example.com",
},
plugins: [hubBlob()],
})Store S3 credentials in Server Env or the provider credential chain used by the S3 SDK.
MinIO
MinIO is the Docker-friendly S3-compatible path. Select it explicitly:
pnpm add files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner// vite.config.ts
export default defineConfig({
blob: {
driver: "minio",
},
plugins: [hubBlob()],
})ViteHub reads common Docker Compose env names:
MINIO_ENDPOINT=http://minio:9000
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=password
BLOB_BUCKET_NAME=vitehub-blobThe Files SDK native MINIO_ACCESS_KEY_ID and MINIO_SECRET_ACCESS_KEY env names are also accepted.
You can also keep the config self-contained:
blob: {
driver: "minio",
accessKeyId: process.env.MINIO_ROOT_USER,
bucket: "vitehub-blob",
endpoint: "http://minio:9000",
forcePathStyle: true,
secretAccessKey: process.env.MINIO_ROOT_PASSWORD,
}Learn more at vitehub.dev.
