Assets

Uploading images and files

Bytes to Blob, metadata to Postgres, and an asset id derived from the bytes — so uploading the same file twice stores it once.

An upload does three things: it stores the bytes, it writes an asset document, and it returns a reference the schema can hold.

ts
const asset = await client.assets.upload('image', file, {
  filename: 'cover.png',
  title: 'Cover',
})

await client.patch('post-1').set({
  coverImage: {
    _type: 'image',
    asset: { _type: 'reference', _ref: asset._id },
    alt: 'Required by this schema',
  },
}).commit()

The id is the bytes

An asset id has the form image-<hash>-<width>x<height>-<ext>, derived from a hash of the content. Two consequences follow, and both are useful:

  • Uploading the same file twice stores it once. The second upload is a metadata write, and the byte store is not touched.
  • Ids are stable across deployments. The demo images in this repository produce the same asset ids in every deployment of it, which is what makes re-seeding a byte-level no-op.

Two upload paths

PathWhenLimit
Server-receiveThe default, for ordinary imagesAbout 4.5 MB, the serverless request body ceiling
Browser-directAnything largerThe Blob store's own limits

The direct path issues a short-lived token, the browser uploads straight to storage, and the API then commits the metadata document. The Studio picks the right path itself.

Deleting

Deleting an asset orphan-marks the row and retains the bytes, so a daily job can reclaim them. That is deliberate: an asset referenced by an old revision is not garbage merely because the current document stopped pointing at it.