bext.dev
DocsFramework Integrations
Framework Integrations3 min read
On this page

Hono Integration

The @bext/hono package provides a Hono middleware that injects bext services into your request context, plus a standalone client for use outside of request handlers.

Installation#

bash
npm install @bext/hono
# or
bun add @bext/hono

Peer dependencies: hono >= 4.0.0

Middleware#

Add the middleware to inject c.var.bext into all handlers:

typescript
import { Hono } from "hono";
import { bextMiddleware, type BextEnv } from "@bext/hono";

const app = new Hono<BextEnv>();

// Add bext services to all routes
app.use(bextMiddleware());

app.post("/api/products", async (c) => {
  const product = await createProduct(c.req.json());

  // Invalidate cached product pages
  await c.var.bext.cache.invalidateTag("products");

  // Notify connected clients
  await c.var.bext.realtime.publish("products", { action: "created", product });

  return c.json(product, 201);
});

export default app;

Custom Configuration#

typescript
app.use(bextMiddleware({
  baseUrl: "http://127.0.0.1:3061",  // SDK sidecar URL
  appId: "my-app",                    // App identifier for multi-app isolation
}));

Standalone Client#

For use outside of request handlers (scripts, workers, etc.):

typescript
import { createBextClient } from "@bext/hono";

const bext = createBextClient();

// Same API as c.var.bext
await bext.cache.invalidateTag("products");
await bext.realtime.publish("updates", { type: "deploy" });
await bext.kv.set("config:feature-flags", { darkMode: true });

API Reference#

BextClient#

typescript
interface BextClient {
  cache: BextCache;
  realtime: BextRealtime;
  kv: BextKv;
  queue: BextQueue;
  tasks: BextTasks;
}

Cache#

typescript
interface BextCache {
  invalidateTag(tag: string): Promise<void>;
  invalidatePath(path: string): Promise<void>;
}

Real-Time#

typescript
interface BextRealtime {
  publish(topic: string, data: unknown): Promise<void>;
}

KV Store#

typescript
interface BextKv {
  get<T = unknown>(key: string): Promise<T | null>;
  set(key: string, value: unknown, opts?: { ttl?: number }): Promise<void>;
  delete(key: string): Promise<void>;
}

Queues#

typescript
interface BextQueue {
  push(name: string, payload: unknown, opts?: { delay?: number }): Promise<void>;
  pull<T = unknown>(name: string): Promise<QueueMessage<T> | null>;
  ack(name: string, id: string): Promise<void>;
}

interface QueueMessage<T = unknown> {
  id: string;
  data: T;
  attempts: number;
}

Tasks#

typescript
interface BextTasks {
  register(name: string, definition: { cron: string; command: string }): Promise<void>;
  list(): Promise<TaskInfo[]>;
  cancel(name: string): Promise<void>;
}

interface TaskInfo {
  name: string;
  cron: string;
  command: string;
  registeredAt: number;
  lastRun: number | null;
}

Example: Full CRUD with Caching#

typescript
import { Hono } from "hono";
import { bextMiddleware, type BextEnv } from "@bext/hono";

const app = new Hono<BextEnv>();
app.use(bextMiddleware());

// List products (ISR-cached by bext, invalidated on mutation)
app.get("/api/products", async (c) => {
  const products = await db.products.findMany();
  return c.json(products);
});

// Create product — invalidate cache + push event
app.post("/api/products", async (c) => {
  const data = await c.req.json();
  const product = await db.products.create(data);

  await c.var.bext.cache.invalidateTag("products");
  await c.var.bext.realtime.publish("products", { action: "created", id: product.id });

  return c.json(product, 201);
});

// Delete product — invalidate + event + queue cleanup job
app.delete("/api/products/:id", async (c) => {
  const id = c.req.param("id");
  await db.products.delete(id);

  await c.var.bext.cache.invalidateTag("products");
  await c.var.bext.cache.invalidatePath(`/products/${id}`);
  await c.var.bext.realtime.publish("products", { action: "deleted", id });
  await c.var.bext.queue.push("cleanup", { type: "product-assets", productId: id });

  return c.json({ deleted: true });
});

export default app;

<div class="callout callout-tip"><div class="callout-label">Tip</div><div class="callout-body"><p><code>c.var.bext</code> is only available inside routes that run after <code>bextMiddleware()</code>. For scripts or background workers outside a request context, use <code>createBextClient()</code> instead.</p>
</div></div>

## Related

- [Frameworks overview](/frameworks/overview) — auto-detection rules and when to pick Hono vs PRISM
- [Express integration](/frameworks/express) — same services via `req.bext` middleware
- [Caching](/guides/caching) — how `invalidateTag` / `invalidatePath` interact with bext's edge cache
- [Durable flows](/guides/durable-flows) — orchestrating multi-step work via the queue
- [Scheduled tasks](/capabilities/scheduled) — registering cron tasks from Hono handlers

## Links

- [hono.dev](https://hono.dev) — Hono framework documentation
Edit this page ↗Need a hand? ↗
FIND YOUR NEXT STEP

Start with a topic, a command, or a question.