Blob
ViteHub Blob adds blob storage for images, videos, documents, and other files with hosting-aware driver detection.
Getting started
Install the package
pnpm add https://pkg.pr.new/nuxt-hub/agent/@vitehub/blob@main
npm install https://pkg.pr.new/nuxt-hub/agent/@vitehub/blob@main
yarn add https://pkg.pr.new/nuxt-hub/agent/@vitehub/blob@main
Choose an integration surface
export default defineNuxtConfig({
modules: ['@vitehub/blob/nuxt'],
})
Set a driver
ViteHub auto-configures blob storage from your environment or hosting provider. Keep the same top-level blob key no matter which integration surface you use.
.data/blob.Use the filesystem driver during local development or smoke testing.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'fs',
dir: '.data/my-blob-directory',
},
})
The local filesystem driver is not suitable for production deployments.
:::
Install aws4fetch and set the S3 environment variables.
pnpm add aws4fetch
npm install aws4fetch
yarn add aws4fetch
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key
S3_BUCKET=your-bucket-name
S3_REGION=eu-central-1
S3_ENDPOINT= # optional
Install @vercel/blob, then either deploy on Vercel or set BLOB_READ_WRITE_TOKEN locally.
pnpm add @vercel/blob
npm install @vercel/blob
yarn add @vercel/blob
BLOB_READ_WRITE_TOKEN=your-token
Files stored in Vercel Blob are public. Use another driver if you need private object storage.
Set the driver explicitly and provide the bucket name. ViteHub generates the Wrangler R2 binding for you.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'cloudflare-r2',
bucketName: 'uploads',
jurisdiction: 'eu',
},
})
::
Upload a file
import { blob } from '@vitehub/blob'
export default defineEventHandler(async () => {
return await blob.put('avatars/user-1.txt', 'hello blob', {
contentType: 'text/plain',
})
})
::
Automatic configuration
Registering the integration without a blob config object keeps the setup small while following the same provider order as NuxtHub:
- Explicit
driver S3_*environment variables- Vercel hosting or
BLOB_READ_WRITE_TOKEN - Cloudflare hosting
- Local filesystem
Driver options
You can always pin a driver explicitly.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'fs',
dir: '.data/files',
},
})
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 's3',
accessKeyId: 'your-access-key-id',
secretAccessKey: 'your-secret-access-key',
bucket: 'your-bucket-name',
region: 'eu-central-1',
endpoint: 'https://s3.eu-central-1.amazonaws.com',
},
})
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'vercel-blob',
token: 'your-token',
},
})
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 'cloudflare-r2',
binding: 'BLOB',
bucketName: 'uploads',
jurisdiction: 'eu',
},
})
Docker and Kubernetes deployments
Build container images without blob credentials by deferring S3 environment resolution to runtime.
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
modules: ['@vitehub/blob/nitro'],
blob: {
driver: 's3',
},
})
Set the credentials when the container starts:
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key
S3_BUCKET=your-bucket-name
S3_REGION=eu-central-1
S3_ENDPOINT= # optional
S3_* first and BLOB_READ_WRITE_TOKEN second at runtime before using the filesystem driver.Nuxt Image integration
@nuxt/image can optimize images that you expose through a blob-backed route.
Install @nuxt/image
pnpm add @nuxt/image
Expose blob files on a route
Create a route that serves blobs through blob.serve():
import { createError, defineEventHandler, getRouterParam } from 'h3'
import { blob } from '@vitehub/blob'
export default defineEventHandler(async (event) => {
const pathname = getRouterParam(event, 'pathname')
if (!pathname)
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
return await blob.serve(event, pathname)
})
Use a production image provider only in production
export default defineNuxtConfig({
modules: ['@vitehub/blob/nuxt'],
image: { provider: 'none' },
$production: {
image: {
provider: 'cloudflare',
},
},
})
provider: 'none' so routes like /images/** still resolve directly through your Nitro server.Serve blobs on a route
Control the content types you serve from user-generated blobs to avoid XSS issues.
Add a restrictive CSP header on the serving route when you only need raw file delivery:
import { createError, defineEventHandler, getRouterParam, setHeader } from 'h3'
import { blob } from '@vitehub/blob'
export default defineEventHandler(async (event) => {
const pathname = getRouterParam(event, 'pathname')
if (!pathname)
throw createError({ statusCode: 404, statusMessage: 'Not Found' })
setHeader(event, 'Content-Security-Policy', 'default-src \'none\';')
return await blob.serve(event, pathname)
})