import { Hono } from "hono"; import { cors } from "hono/cors"; import { etag } from "hono/etag"; import { logger } from "hono/logger"; import { prettyJSON } from "hono/pretty-json"; import { html } from "hono/html"; import sharp from "sharp"; const homepage = html` Resize Images API

Resize images API

Try now

Docs

The structure of the API path is:

/api/imgresize/:width/:height/:url

Where width and height can be numbers in pixels or auto.

The URL should be URL encoded, which can be done in JS with encodeURIComponent. See MDN ref.

Example for https://memori.ai/logo.png:

https%3A%2F%2Fmemori.ai%2Flogo.png

Then call the API as, for example:

/api/imgresize/200/200/https%3A%2F%2Fmemori.ai%2Flogo.png

You can also specify a format using the querystring ?format= and indicating one of the following: avif, gif, heif, jpeg, jpg, jp2, pdf, png, svg, tiff, webp. Note: Experimental!

`; const app = new Hono(); app.use(prettyJSON()); app.use(etag(), logger()); app.use("/api/*", cors()); app.get("/", (c) => { return c.html(homepage); }); app.post("/api/preview", async (c) => { const body = await c.req.parseBody(); c.header("Cache-Control", "s-maxage=31536000, stale-while-revalidate"); const url = body["url"]; if (!url || typeof url !== "string") { c.status(400); return c.json({ error: "No URL provided" }); } const width = body["width"]; const height = body["height"]; const w = width && width !== "auto" ? Number(width) : 200; const h = height && height !== "auto" ? Number(height) : 200; return c.redirect(`/api/imgresize/${w}/${h}/${encodeURIComponent(url)}`); }); app.get("/api/imgresize/:width/:height/:url", async (c) => { const { width, height, url } = c.req.param(); const format = c.req.query("format"); c.header("Cache-Control", "s-maxage=31536000, stale-while-revalidate"); c.header("Content-Type", `image/jpeg`); const decodedUrl = decodeURIComponent(url as string); const readStream = await fetch(decodedUrl, { cache: "no-cache" }); const input = Buffer.from(await readStream.arrayBuffer()); const w = width && width !== "auto" ? Number(width) : undefined; const h = height && height !== "auto" ? Number(height) : undefined; const outputBuffer = await sharp(input) // outputBuffer contains JPEG image data // no wider and no higher than w and h pixels // and no larger than the input image .resize({ width: w, height: h, fit: sharp.fit.inside, withoutEnlargement: true, }) .toFormat( format && sharp.format[format as keyof typeof sharp.format] !== undefined ? sharp.format[format as keyof typeof sharp.format] : sharp.format.jpeg ) .toBuffer(); c.status(200); return c.body(outputBuffer); }); export default { port: Bun.env.PORT || 8787, fetch: app.fetch, };