Vault
Guides

Browser uploads

Let users upload directly from the browser with signed URLs.

Upload bytes straight from the browser to Vault — without proxying them through your server and without exposing your API key.

1. Issue a signed URL on your server

// app/api/upload-url/route.ts (your backend)
import { Storage } from "@vault/storage";

const storage = new Storage({ apiKey: process.env.VAULT_API_KEY });

export async function POST(req: Request) {
  const { key } = await req.json();
  const { url } = await storage.objects.createSignedUrl({
    bucket: "uploads",
    key,
    method: "PUT",
    expiresIn: "10m",
    maxBytes: 10_000_000,
  });
  return Response.json({ url });
}

2. Upload from the client

async function upload(file: File) {
  const key = `users/${crypto.randomUUID()}/${file.name}`;
  const { url } = await fetch("/api/upload-url", {
    method: "POST",
    body: JSON.stringify({ key }),
  }).then((r) => r.json());

  await fetch(url, { method: "PUT", body: file });
  return key;
}
Your server issues a short-lived signed PUT URL for a specific key.
The browser uploads the file bytes directly to that URL.
Store the returned key in your database to reference the object later.

Serve public objects (or issue signed GET URLs) to display them afterwards.

On this page