10 KiB
Tasks
- understand
picsumapi and what exactly I need from it - understand what exactly is meant by caching (it seems the assignment only wants to cache explicit calls to
picsum, but there's also image browser caching to consider) - What exactly is Material UI. I assume this is a library of styled components (probably with a React lib available too)
- Bonus: Generating API descriptions via AI. This seems much more complex than the rest.
Picsum API
TASK: understand picsum api
specifically the list endpoint
Seems that in the background they have this huge list of images, that's stable (i.e. each request given the same input-payload, returns the same list of images).
They use pages. What's a page?
So is this like
type Pages =
List Page
type Page =
List ImageRef
Is there some sort of a limit on a given page? Like at-most 30 images per page or something like that? How does that work?
Basic Test
The https://picsum.photos/200 redirects to e.g. https://fastly.picsum.photos/id/338/200/200.jpg?hmac=5S5SeR5xW8mbN3Ml7wTTJPePX392JafhcFMGm7IFNy0 which is the image (jpeg).
This basically means:
{
id: 338,
width: 200,
height: 200,
format: "jpg",
}
it also includes a hash of the payload/image. Why? Apparently this is for CDN caching.
Pages
Ok, set limit = 10 - that's the page size. Experimentally verified that pages start at 1 not 0.
https://picsum.photos/v2/list?limit=10&page=1
This is what's returned in the 3rd page (with limit=10).
[
{
"id": "30",
"author": "Shyamanta Baruah",
"width": 1280,
"height": 901,
"url": "https://unsplash.com/photos/aeVA-j1y2BY",
"download_url": "https://picsum.photos/id/30/1280/901"
},
{
"id": "31",
"author": "How-Soon Ngu",
"width": 3264,
"height": 4912,
"url": "https://unsplash.com/photos/7Vz3DtQDT3Q",
"download_url": "https://picsum.photos/id/31/3264/4912"
},
{
"id": "32",
"author": "Rodrigo Melo",
"width": 4032,
"height": 3024,
"url": "https://unsplash.com/photos/eG3k60PrTGY",
"download_url": "https://picsum.photos/id/32/4032/3024"
},
{
"id": "33",
"author": "Alejandro Escamilla",
"width": 5000,
"height": 3333,
"url": "https://unsplash.com/photos/LBI7cgq3pbM",
"download_url": "https://picsum.photos/id/33/5000/3333"
},
{
"id": "34",
"author": "Aleks Dorohovich",
"width": 3872,
"height": 2592,
"url": "https://unsplash.com/photos/zZvsEMPxjIA",
"download_url": "https://picsum.photos/id/34/3872/2592"
},
{
"id": "35",
"author": "Shane Colella",
"width": 2758,
"height": 3622,
"url": "https://unsplash.com/photos/znM0ujn2RUA",
"download_url": "https://picsum.photos/id/35/2758/3622"
},
{
"id": "36",
"author": "Vadim Sherbakov",
"width": 4179,
"height": 2790,
"url": "https://unsplash.com/photos/osSryggkso4",
"download_url": "https://picsum.photos/id/36/4179/2790"
},
{
"id": "37",
"author": "Austin Neill",
"width": 2000,
"height": 1333,
"url": "https://unsplash.com/photos/erTjj730fMk",
"download_url": "https://picsum.photos/id/37/2000/1333"
},
{
"id": "38",
"author": "Allyson Souza",
"width": 1280,
"height": 960,
"url": "https://unsplash.com/photos/JabLtzJl8bc",
"download_url": "https://picsum.photos/id/38/1280/960"
},
{
"id": "39",
"author": "Luke Chesser",
"width": 3456,
"height": 2304,
"url": "https://unsplash.com/photos/pFqrYbhIAXs",
"download_url": "https://picsum.photos/id/39/3456/2304"
}
]
Ok so basically:
type ImageId = string // TODO: symbol
type ImageRef = string
type Image = {
id: ImageId,
author: string,
width: number,
height: number,
url: string,
download_url: ImageRef
}
Caching
Can I specify sizes better? What about caching?
Assignment says that I should cache the requests to the picsum api. This is simple enough.
I just need to cache the
https://picsum.photos/v2/list?limit=10&page=1
request. Probably something like useMemo would be sufficient (or I can do a custom hook).
But what about caching of images themselves? Does browser handle this automagically? I'm pretty sure that it does, I definitely thought about this before in itravel. Unfortunately I don't remember the conclusion I reached /facepalm This is just HTTP GET caching, so should be fine.
To confirm:
http --follow --headers https://picsum.photos/id/30/300/200
gives
- First request
HTTP/1.1 200 OK
Accept-Ranges: bytes
Age: 818206
Cache-Control: public, max-age=2592000, stale-while-revalidate=60, stale-if-error=43200, immutable
Connection: keep-alive
Content-Disposition: inline; filename="30-300x200.jpg"
Content-Length: 10181
Content-Type: image/jpeg
Date: Thu, 14 May 2026 12:48:29 GMT
Picsum-Id: 30
Server: nginx
Timing-Allow-Origin: *
Vary: Origin
Via: 1.1 varnish
X-Cache: HIT
X-Cache-Hits: 0
X-Served-By: cache-fra-eddf8230047-FRA
X-Timer: S1778762910.527297,VS0,VE1
- Second request
HTTP/1.1 200 OK
Accept-Ranges: bytes
Age: 818234
Cache-Control: public, max-age=2592000, stale-while-revalidate=60, stale-if-error=43200, immutable
Connection: keep-alive
Content-Disposition: inline; filename="30-300x200.jpg"
Content-Length: 10181
Content-Type: image/jpeg
Date: Thu, 14 May 2026 12:48:57 GMT
Picsum-Id: 30
Server: nginx
Timing-Allow-Origin: *
Vary: Origin
Via: 1.1 varnish
X-Cache: HIT
X-Cache-Hits: 1
X-Served-By: cache-fra-eddf8230177-FRA
X-Timer: S1778762937.389504,VS0,VE1
Cache-Control seems to indicate this is cached in CDN. I also need to test the browser behaviour.
Let's try to test the browser img.src behaviour.
var img = new Image();
document.body.appendChild(img);
Then
img.src = "https://picsum.photos/id/30/300/200"; // makes `GET https://picsum.photos/id/30/300/200` which results 302, then `https://fastly.picsum.photos/id/30/300/200.jpg?hmac=VXfU9CUIzgRHYSjKg8FAl7JDQIea3VOfR8f98SpfXbo` which gives 200
img.src = "https://picsum.photos/id/31/300/200"; // makes `GET https://picsum.photos/id/31/300/200` which results 302, then `https://fastly.picsum.photos/id/31/300/200.jpg?hmac=WPPS-sLIpuyg7q2io4x82NuBdN-FK1W5uDG3iPVzi2g` which gives 200
img.src = "https://picsum.photos/id/30/300/200"; // makes `GET https://picsum.photos/id/30/300/200` which resulst immediately into 200. No further requests are made
Cool, as expected browser caches images nicely too.
Modal
"When an image is clicked, open a modal showing the image in a bigger size."
Will have to setup basic modal, but that's fine.
The point here is that this will just mount a new component (the modal) an in it will be an <img> with a different source (the sizes are gonna be different). I don't have to do any manual fetch.
Material UI
Yep, it is available as a react library:
- it even has a modal component
Dialogwhich I could use. Buttonfor the prev/nextCircularProgressfor loading
Ideally I would love to use something like a fake list of images that are loading when making the getImages request to picsum api.
Need to decide if the current page will just have 10 stable (under prev/next) react components, or will they re-mount from scratch.
Yeah, I'm pretty sure these will not remount. What would be the point of that? The images would get stable keys (let's say 1 to 10). But this seems overcomplicated. Let's do something simpler key={imageId}. You shouldn't really care about the React remounting all of this. It's not perceptible by humans.
When a page is changed, the state that represents them is set to loading (that's when we don't even have the url computed yet).
But suppose we do compute the src - so under the stable keys the underlying <img> DOM node's src will get mutated and either the cached image is gonna be displayed, or GET request is gonna be made by the browser.
This is I guess a bit weird... the image is still loading in some sense... Ideally I would prefer to set it to loaded once the real image is loaded for real.
Fuck it. the states should be
| LoadingPage // waiting for the `loadImages`. Here display a "ghost" grid of 10 images.
| Loaded {
, imageId: List<ImageId>
, modal: Option<{ openedImage: ImageId }> // by default modal is not open, but when an image is clicked, it sets this to `some`
}
AI generated descriptions
Now this is a bit weird. I'm kinda sceptical that there's a freely available api that would generate image descriptions. I would need to send the image to the model (I guess there are apis that accept a simple url, I'm pretty sure I don't have to encode the image into binary and send it like that to the model directly).
Another (insane) option would be to bundle some very simple model in the client (like a wasm image), but I bet anything useful is still like atleast ~500 MB lol.
Another option would be to self-host it, but that's kinda a lot of work (setting up the server, exposing api, talking to the model locally, self-hosting. Note even sure my minipc could handle any non-trivial model)
The non-insane option is ofcourse to use some externally hosted model that exposes api endpoint that takes in image url and responds with a description. The problem again is: I shouldn't really deploy that one then. I would have to expose an API key to the public facing internet. Or even have in github /facepalm, which is terrible. But then again, this is just atmost a 4 hour task. I could self-host a tiny api endpoint though on my minipc. But I don't really want to introduce a new server functionality - my pages right now are completely static.
- The easiest reasonable solution would be to find a free (yeah, right) inference service without needing an API key. Unlikely
- Another option would be to get an API key for image-to-text service, and setup a serverless function that would just proxy
image_urlbut it would hide the API key. I would hardcode the api endpoint url in github. I think this is fine for this particular assignment. This is probably what I'm gonna use. The only problem I guess could be CORS. When self-hosting TODO: Take a look at cloudflare workers. TODO: Decide which image-to-text model to use.