Direct upload
A direct upload sends a file straight from the browser or client to storage, bypassing your application server. The backend issues a presigned URL, then the client PUTs the bytes to it. This avoids proxying large files through your server, cutting load, memory use, and latency.
In the naive approach, an upload travels twice: client → your server → storage. Your server has to buffer or stream the whole file, which burns memory and request time and makes large uploads a scaling headache. Direct upload removes the middle hop.
The flow is: (1) the client asks your API for permission, (2) your backend mints a presigned URL scoped to a single object and method, (3) the client PUTs the bytes straight to storage, and (4) the client (or a webhook) tells your backend the upload finished so it can record metadata. Your server only ever handles small JSON, never the file itself.
The two things to get right are CORS (so the browser is allowed to PUT cross-origin) and a finalize/confirm step (so your database knows the object now exists).
// 1. ask your API for a URL, then PUT the file straight to storage
const { url } = await api.getUploadUrl(file.name);
await fetch(url, { method: "PUT", body: file });