File upload flow
@pbvex/client does not expose a direct file upload method. A server-side mutation or action first calls ctx.storage.generateUploadUrl() (see the Storage guide); the browser then uploads directly to that one-time URL.
Server mutation that returns an upload URL
ts
// pbvex/files.ts
import { mutation } from './_generated/server.js';
export const getUploadUrl = mutation({
handler: async (ctx) => {
const url = await ctx.storage.generateUploadUrl();
return { url };
},
});Client upload flow
ts
import { Client, type StorageId, type StorageUploadResponse } from '@pbvex/client';
import { api } from './pbvex/_generated/api.js';
const client = new Client('http://localhost:8090');
async function uploadFile(file: File) {
// 1. Request a one-time upload URL from the server.
const { url } = await client.mutation(api.files.getUploadUrl);
// 2. Upload the file directly to the returned URL.
const upload = await fetch(url, {
method: 'POST',
body: file,
headers: {
'Content-Type': file.type || 'application/octet-stream',
},
});
if (!upload.ok) {
throw new Error(`Upload failed: ${upload.status}`);
}
// 3. The upload endpoint returns an opaque StorageId.
const { storageId } = await upload.json() as StorageUploadResponse;
// 4. Persist the storage id in a record.
await client.mutation(api.files.attach, { storageId });
}
async function downloadUrl(storageId: StorageId) {
return client.query(api.files.getUrl, { storageId });
}Limits
ClientLimits.maxUploadBytesis accepted for forward compatibility but is not enforced by the client.- The actual upload size limit is enforced by the backend (
--storageMaxFileSize/PBVEX_STORAGE_MAX_FILE_SIZE). - The upload endpoint returns a structured JSON error on failure. It is a direct
fetch, so inspect that response yourself;PBVexErrorwrapping applies to calls made throughClient.
Notes
- Upload with
POST, a raw request body, and a validContent-Type. The public HTTP contract is the same regardless of PocketBase's configured file backend. StorageIdis an opaque branded string; pass it through without constructing or parsing.- Delete a stored object through a server mutation or action that calls
ctx.storage.delete(id).