Rebasely Beta: data may be purged

examples/storefront Public

Markdown · 26 lines · 1.2 KiB · at 0d7e6e2

---id: product-imagestype: conceptowner: justincomponent: storefront-apidescribes:  - src/images.tsreviewed: 2026-10-01---# Product imagesEach product has at most one image. It lives in the `storefront-images` R2 bucket, and the product row keeps its key in `image_key`. The API turns the key into `image_url`.## Upload`PUT /admin/products/:id/image` takes the raw bytes with a `content-type` of JPEG, PNG, WebP or AVIF, up to 5 MiB. The key is `products/<id>/<hash>.<ext>`, where the hash is the first 8 bytes of the SHA-256 of the image. Because the key depends on the content:- a new image always gets a new URL, so nothing has to be purged from a cache;- the object can be stored with `cache-control: public, max-age=31536000, immutable`;- uploading the same file twice is harmless.The previous object is left in the bucket. A cart page or a CDN may still hold its URL, and images are small enough that keeping them costs little.## Serving`GET /images/<key>` reads the object from R2 and returns it with the stored headers and its ETag, or a 304 when `If-None-Match` matches. Only keys under `products/` are served. See [[0002-serve-images-through-the-api]] for why the Worker serves them.