Storage
Upload, serve, and delete files in VitNode with pluggable storage adapters - local disk, AWS S3, Cloudflare R2, or Supabase Storage.
VitNode provides an integrated storage layer accessible via c.get("storage"). It handles file validation, automatic WebP image optimization, database indexing in core_files, and persistent storage across multiple backends.
Quick start
1. Register a Storage Adapter
In apps/api/src/vitnode.api.config.ts, configure the local disk adapter:
import { LocalStorageAdapter } from "@vitnode/core/api/adapters/storage/local"
import { buildApiConfig } from "@vitnode/core/vitnode.config"
export const vitNodeApiConfig = buildApiConfig({
storage: {
adapter: LocalStorageAdapter(),
},
})2. Upload Files in an API Route
Handle uploads in a Hono route using c.get("storage").upload():
import { z } from "@hono/zod-openapi"
import { buildRoute } from "@vitnode/core/api/lib/route"
export const uploadCoverRoute = buildRoute({
pluginId: "blog",
route: {
method: "post",
path: "/cover",
request: {
body: {
content: {
"multipart/form-data": {
schema: z.object({
file: z.instanceof(File),
}),
},
},
},
},
},
handler: async (c) => {
const { file } = await c.req.parseBody()
const uploaded = await c.get("storage").upload({
file: file as File,
folder: "covers",
maxBytes: 5 * 1024 * 1024, // 5 MB
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"],
})
return c.json(uploaded)
},
})upload() saves the file, creates a record in core_files, and returns { id, url, width, height, size }.
Supported Storage Adapters
Local Disk
Stores files in public/uploads for local development
AWS S3 / Cloudflare R2
Scalable object storage via @vitnode/s3
Supabase Storage
Native Supabase bucket storage
Custom Storage Adapter
Implement your own storage backend
Image Optimization
The storage service can automatically convert uploaded images to WebP and constrain dimensions:
const uploaded = await c.get("storage").upload({
file,
folder: "avatars",
convertImagesToWebp: true,
maxDimensions: { width: 1200, height: 1200 },
})Deleting Files
Remove files and clean up storage using delete():
await c.get("storage").delete({ fileId: 42 })This removes the file from the configured storage bucket and deletes its row from core_files.
Verifying in AdminCP
Test your storage adapter anytime in the AdminCP:
- Navigate to System → Integrations (
/admin/core/system/integrations). - On the Storage card, click Test Storage to verify bucket credentials and upload capabilities.
- Inspect uploaded files anytime under System → Files (
/admin/core/system/files).