---
title: "Bun"
description: "Generate dynamic OG images and on-demand graphics in Bun with the Imejis.io API. A secure Bun.serve handler that keeps your API key server-side and returns a cached image."
url: "https://www.imejis.io/apis/bun"
published: "2026-07-27"
---

# Bun

## Bun

Generate a unique OG image for every page, or any on-demand graphic, from a single Imejis template. The pattern below uses a `Bun.serve` handler as a thin proxy: your API key stays on the server, and the browser (and social crawlers) only ever see your own URL.

Set two environment variables first: `IMEJIS_API_KEY` (from your dashboard) and `IMEJIS_DESIGN_ID` (any design's ID). Bun loads a `.env` file automatically and exposes it on `Bun.env`.

```bash title=".env"
IMEJIS_API_KEY=your_api_key
IMEJIS_DESIGN_ID=your_design_id
```

### 1. A handler that renders the image

This handler takes a `title` query param, POSTs it to Imejis as a `component.property` override, and returns the finished image. Reading the key from `Bun.env` keeps it server-side, so it never reaches the client.

```typescript showLineNumbers title="server.ts"
Bun.serve({
  async fetch(req) {
    const url = new URL(req.url)
    if (url.pathname !== "/og")
      return new Response("Not found", { status: 404 })

    const title = url.searchParams.get("title") ?? "Untitled"

    const res = await fetch(
      `https://render.imejis.io/v1/${Bun.env.IMEJIS_DESIGN_ID}`,
      {
        method: "POST",
        headers: {
          "dma-api-key": Bun.env.IMEJIS_API_KEY as string,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ "headline.text": title }),
      }
    )

    return new Response(res.body, {
      headers: {
        "Content-Type": "image/jpeg",
        // Renders are deterministic, so cache aggressively at the edge/CDN.
        "Cache-Control": "public, immutable, no-transform, max-age=31536000",
      },
    })
  },
})
```

Run it with `bun run server.ts`.

### 2. Point your metadata at it

Reference the handler from your HTML `<head>`, passing each page's real data. Social platforms fetch `/og?title=…` from your own domain, no key exposed.

```html showLineNumbers title="Page head"
<meta
  property="og:image"
  content="https://your-site.com/og?title=Hello%20world"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
  name="twitter:image"
  content="https://your-site.com/og?title=Hello%20world"
/>
```

**Override any field.** Each dynamic component exposes keys like `headline.text`, `title.color`, or `image.image`. Send only the ones you want to change. A design's overridable fields are listed in its Share / API panel.
