Skip to content

Building Honcanator: The AI Goose Generator

by Mari Steiner on Nov 14, 2024

Recently we needed some example apps to showcase how to build a HONC app with Fiberplane Studio for our November Honcathon, so I decided to build Honcanator: The AI goose generator.

The example app is pretty simple and uses a standard HONC setup (Hono, Drizzle, Neon, and Cloudflare Workers).

To create and store images, we also needed to use two Cloudflare services:

  • Cloudflare R2 as object storage (like AWS S3)
  • Cloudflare AI to generate images

Let’s take a quick look at how I’ve built this app.

Configuring the basics

In order to use Cloudflare services like R2 and AI, we have to configure some additional Cloudflare bindings in our Worker. That is as simple as editing the wrangler.toml:

wrangler.toml
name = "honc-neon-template"
compatibility_date = "2024-10-28"
compatibility_flags = [ "nodejs_compat" ]
[observability]
enabled = true
[[r2_buckets]]
binding = "R2_BUCKET"
bucket_name = "geese"
[ai]
binding = "AI"

Then we can add it to our bindings in Hono:

src/index.ts
type Bindings = {
DATABASE_URL: string;
R2_BUCKET: R2Bucket;
AI: Ai;
};

The R2Bucket and Ai types are Cloudflare special types which will give you a typed interface for interacting with these services.

Routes

The majority of our routes were simple CRUD routes which simply query the database and return some stuff from it so I won’t bore you with the boring part, lets get to the actual interesting route: The creation endpoint.

The first few lines of the POST /api/geese/:name route are simply a check to see whenever a goose with that name already exists.

src/index.ts
app.get("/api/geese/:name", async (c) => {
const { name } = c.req.param();
const sql = neon(c.env.DATABASE_URL);
const db = drizzle(sql);
const goose = await db.select().from(geese).where(eq(geese.name, name));
if (goose.length === 0) {
return c.json({ error: "doesnt_exist" }, 404);
}
//...
})

After those checks are done, we get to the really interesting part: Making geese!

Image generation

This is where Cloudflare really shines. Creating an image is as simple as three lines of code:

src/index.ts
const model = "@cf/black-forest-labs/flux-1-schnell" as BaseAiTextToImageModels;
const prompt = `Please generate a image of a goose. Its name is ${name}. Make it in the style of comic or anime please`;
const response = await c.env.AI.run(model, {
prompt,
});

The list of models available on Cloudflare AI can be found here. The list of image generation models is pretty small at the moment, there is currently a few Stable Diffusion models and Flux-1-Schnell model, which we are using in this example.

The response from the AI call comes back as a base64 encoded string, so we’ll use the built-in Node.js Buffer to get something that we can work with:

src/index.ts
const base64image = response.image;
const buffer = Buffer.from(base64image, "base64");

Saving to object storage

With our generated image now available to us as a Buffer, putting it into R2 is as simple as writing one line of code:

src/index.ts
await c.env.R2_BUCKET.put(`${name}.png`, buffer);

This shows how powerful the Hono bindings for Cloudflare are, we have just generated an image and stored it in object storage with like 6 lines of code.

Retrieving it is as simple as:

src/index.ts
const image = await c.env.R2_BUCKET.get(`${name}.png`);

which comes in handy for our GET /api/geese/:name route.

Using Fiberplane Studio, we can inspect the generated image for any of our geese and see a trace of the request, with the Neon database call and the call to R2, in the timeline.

Fiberplane Studio trace of the Honcanator app

Parting words

In conclusion, making a small app which generates images and stores them in object storage and a database really is no big feat with the HONC stack, which means you’ll be able to build powerful goose-themed apps in no time.

To wrap things up, the code for everything discussed in this blog post can be found on GitHub, available for you to adapt and build upon how you wish: