Skip to content

Storage overview

Storage keeps files in buckets that belong to your project. A bucket is public, so anyone with a file’s URL can read it, or private, so files are read through signed URLs that expire. Storage policies decide who may upload, read, change and delete files in each bucket.

Use upload() from @vaifstudio/client/storage. It sends a small file in one request, and switches to a multipart upload for files of 5 MiB or more, sending the parts in parallel:

import { createVaifClient } from "@vaifstudio/client";
import { upload } from "@vaifstudio/client/storage";
const vaif = createVaifClient({
baseUrl: "https://api.vaif.studio",
projectId: process.env.VAIF_PROJECT_ID,
apiKey: process.env.VAIF_PUBLISHABLE_KEY,
});
const result = await upload(vaif, "avatars", "users/42/avatar.png", file, {
contentType: "image/png",
onProgress: (e) => console.log(`${Math.round(e.fraction * 100)}%`),
});
console.log(result.key, result.sizeBytes, result.multipart);

The arguments are the client, the bucket name, the file’s key in the bucket, and a Blob or File. The key needs the storage scope, which a project’s publishable key has. See Uploads in depth for canceling an upload, resuming after a crash, and tuning part size and concurrency.

// Private bucket: a signed URL that expires (in seconds; the default is 3600)
const signed = await vaif.storage.createSignedUrl("users/42/avatar.png", 600, { bucket: "avatars" });
// Public bucket: a plain URL, no signing
const publicUrl = vaif.storage.getPublicUrl("users/42/avatar.png", { bucket: "avatars" });
// Or download the bytes
const bytes = await vaif.storage.download("users/42/avatar.png", { bucket: "avatars" });
console.log(signed, publicUrl, bytes.size);

@vaifstudio/client/storage/browser uploads straight from a file input or a drop zone:

import { uploadFromInput, createDropZone } from "@vaifstudio/client/storage/browser";
const input = document.querySelector<HTMLInputElement>("#avatar")!;
input.addEventListener("change", async () => {
const results = await uploadFromInput(vaif, input, { bucket: "avatars", keyTemplate: "users/42/${name}" });
console.log(results.map((r) => r.key));
});
const zone = createDropZone(vaif, document.getElementById("drop")!, {
bucket: "inbox",
onResult: (result, dropped) => console.log(dropped.name, "->", result.key),
onError: (error, dropped) => console.error(dropped.name, error),
});
// When the component unmounts:
zone.destroy();

keyTemplate replaces ${name} with the file’s name. These helpers keep resume state in the browser’s local storage by default; pass resume: null to turn that off.

Listing, deleting, copying and moving files, and managing buckets, are console operations: they need a project developer’s console access token, not the publishable key. Run them from your own server or scripts:

import { createVaifClient } from "@vaifstudio/client";
const admin = createVaifClient({
baseUrl: "https://api.vaif.studio",
projectId: process.env.VAIF_PROJECT_ID,
accessToken: process.env.VAIF_ACCESS_TOKEN, // a console access token, server only
});
const { files, hasMore, nextCursor } = await admin.storage.list({ bucket: "avatars", prefix: "users/42/" });
console.log(files.length, hasMore, nextCursor);
await admin.storage.delete("users/42/old.png", { bucket: "avatars" });
const { deleted, errors } = await admin.storage.deleteMany(["a.png", "b.png"], { bucket: "avatars" });
console.log(deleted, errors);

You can also browse, upload and delete files on the Storage page in the console.

How much storage a project can hold depends on its plan. See Plans.