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

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:




const app = new Hono();

// 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

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.):



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

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

Cache

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

Real-Time

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

KV Store

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

Queues

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

interface QueueMessage {
  id: string;
  data: T;
  attempts: number;
}

Tasks

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




const app = new Hono();
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;