Added zod. Wrapped picsum API.

This commit is contained in:
Yura Dupyn 2026-05-14 19:05:18 +02:00
parent 2137fcc776
commit ae6451e052
3 changed files with 78 additions and 11 deletions

4
package-lock.json generated
View file

@ -12,7 +12,8 @@
"@emotion/styled": "^11.14.1", "@emotion/styled": "^11.14.1",
"@mui/material": "^9.0.1", "@mui/material": "^9.0.1",
"react": "^19.2.6", "react": "^19.2.6",
"react-dom": "^19.2.6" "react-dom": "^19.2.6",
"zod": "^4.4.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
@ -3472,7 +3473,6 @@
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"

View file

@ -15,7 +15,8 @@
"@emotion/styled": "^11.14.1", "@emotion/styled": "^11.14.1",
"@mui/material": "^9.0.1", "@mui/material": "^9.0.1",
"react": "^19.2.6", "react": "^19.2.6",
"react-dom": "^19.2.6" "react-dom": "^19.2.6",
"zod": "^4.4.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",

View file

@ -1,12 +1,78 @@
import { CssBaseline, Typography } from '@mui/material'; import { z } from 'zod'
// TODO: Use Material UI later.
// TODO: Improve error-handling. Introduce some proper server response.
type ImageId = string
type ImageRef = string
type Image = {
id: ImageId,
dimension: Dimension,
source: ImageRef,
}
type Dimension = {
width: number,
height: number,
}
// WARNING: page starts at 1
type Pagination = {
page: number,
limit: number, // page size
}
const Pagination = {
init(): Pagination { return { page: 1, limit: 10 } },
}
// === api ===
const picsumApiImageSchema = z.object({
id: z.string(),
author: z.string(),
width: z.number(),
height: z.number(),
url: z.string(),
download_url: z.string(), // WARNING: the api-endpoint returns "url" and "download_url". You definitely want "download_url" for image sources.
})
type PicsumApiImage = z.infer<typeof picsumApiImageSchema>
async function getPicsumImages({ page, limit }: Pagination): Promise<PicsumApiImage[]> {
const response = await fetch(`https://picsum.photos/v2/list?limit=${limit}&page=${page}`)
if (!response.ok) {
throw new Error(`Failed to fetch images: ${response.status}`)
}
const json: unknown = await response.json()
const data = z.array(picsumApiImageSchema).parse(json)
return data
}
async function getImages(pagination: Pagination): Promise<Image[]> {
const data = await getPicsumImages(pagination)
return data.map(({ id, download_url, width, height }) => ({ id: id as ImageId, dimension: { width, height }, source: download_url as ImageRef }))
}
async function getImageIds(pagination: Pagination): Promise<ImageId[]> {
const data = await getPicsumImages(pagination)
return data.map(({ id }) => id)
}
// Use this for `<img src=... />`
function getImageSource(id: ImageId, dimension: Dimension): ImageRef {
return `https://picsum.photos/id/${id}/${dimension.width}/${dimension.height}`;
}
// test
// (async () => {
// const imgs = await getImages({ page: 2, limit: 10 })
// console.log(imgs)
// })()
export default function App() { export default function App() {
return ( return (
<> <h1>hello world</h1>
<CssBaseline /> )
<main>
<Typography variant="h1">Hello world</Typography>
</main>
</>
);
} }