Hono integration for saga-bus (edge runtime compatible)
npm install @saga-bus/honoHono integration for saga-bus, designed for edge runtimes (Cloudflare Workers, Deno, Bun).
``bash`
npm install @saga-bus/hono honoor
pnpm add @saga-bus/hono hono
- Edge Runtime Compatible: Works with Cloudflare Workers, Deno, Bun
- Context Variables: Type-safe c.var.bus and c.var.correlationId
- Health Handler: Factory for health check routes
- Error Handler: Saga-specific error responses
- No Node.js Dependencies: Uses simple UUID generator
`typescript
import { Hono } from "hono";
import { createBus } from "@saga-bus/core";
import {
sagaBusMiddleware,
sagaErrorHandler,
createHealthHandler,
} from "@saga-bus/hono";
const bus = createBus({ / config / });
await bus.start();
const app = new Hono();
// Error handler first
app.use("*", sagaErrorHandler());
// Attach bus to context
app.use("*", sagaBusMiddleware({ bus }));
// Health check
app.get("/health", createHealthHandler({ bus }));
// Your routes
app.post("/orders", async (c) => {
const bus = c.get("bus");
const correlationId = c.get("correlationId");
await bus.publish({
type: "CreateOrder",
payload: await c.req.json(),
});
return c.json({ correlationId });
});
export default app;
`
Creates middleware that attaches the bus instance and correlation ID to context.
`typescript
interface SagaBusHonoOptions {
/* The bus instance to attach /
bus: Bus;
/* Header name for correlation ID (default: "x-correlation-id") /
correlationIdHeader?: string;
/* Whether to generate correlation ID if not present (default: true) /
generateCorrelationId?: boolean;
/* Custom correlation ID generator /
correlationIdGenerator?: () => string;
}
`
Error handler middleware for saga-related errors.
- SagaTimeoutError: Returns 408 Request Timeout
- ConcurrencyError: Returns 409 Conflict
Creates a health check handler.
`typescript
interface HealthCheckOptions {
/* The bus instance to check /
bus: Bus;
/* Additional health checks /
checks?: Array<{
name: string;
check: () => Promise
}>;
}
`
Type definition for Hono context variables.
`typescript`
interface SagaBusEnv extends Env {
Variables: {
bus: Bus;
correlationId: string;
};
}
`typescript
import { Hono } from "hono";
import { sagaBusMiddleware, SagaBusEnv } from "@saga-bus/hono";
const app = new Hono
app.use("*", sagaBusMiddleware({ bus }));
app.post("/messages", async (c) => {
const bus = c.get("bus");
const correlationId = c.get("correlationId");
await bus.publish({
type: c.req.query("type") || "Message",
payload: await c.req.json(),
});
return c.json({ success: true, correlationId });
});
`
`typescript
import { createHealthHandler } from "@saga-bus/hono";
app.get("/health", createHealthHandler({
bus,
checks: [
{
name: "database",
check: async () => {
await db.query("SELECT 1");
return true;
},
},
],
}));
`
`typescript
import { Hono } from "hono";
import { sagaBusMiddleware, SagaBusEnv } from "@saga-bus/hono";
import { createBus } from "@saga-bus/core";
interface Env {
DB: D1Database;
}
const app = new Hono
// Create bus per-request from environment
app.use("*", async (c, next) => {
const bus = createBusFromEnv(c.env);
return sagaBusMiddleware({ bus })(c, next);
});
app.post("/messages", async (c) => {
await c.get("bus").publish({ type: "Message", payload: {} });
return c.json({ ok: true });
});
export default app;
`
`typescript
import { Hono } from "https://deno.land/x/hono/mod.ts";
import { sagaBusMiddleware, SagaBusEnv } from "@saga-bus/hono";
const app = new Hono
app.use("*", sagaBusMiddleware({ bus }));
Deno.serve(app.fetch);
`
`typescriptreq-${Date.now()}-${Math.random().toString(36).slice(2)}
app.use("*", sagaBusMiddleware({
bus,
correlationIdHeader: "x-request-id",
correlationIdGenerator: () => ,`
}));
The package provides full type safety for context variables:
`typescript
import { Hono } from "hono";
import { SagaBusEnv } from "@saga-bus/hono";
// Use SagaBusEnv for typed context
const app = new Hono
app.get("/", (c) => {
// c.get("bus") is typed as Bus
const bus = c.get("bus");
// c.get("correlationId") is typed as string
const correlationId = c.get("correlationId");
return c.json({ correlationId });
});
`
`typescript
app.use("*", sagaErrorHandler());
app.get("/order/:id", async (c) => {
// If this throws a ConcurrencyError, client gets 409
await c.get("bus").publish({
type: "UpdateOrder",
payload: { id: c.req.param("id") },
});
return c.json({ updated: true });
});
`
Response on ConcurrencyError:
`json``
{
"error": "Concurrency Conflict",
"message": "Expected version 1, but found 2",
"correlationId": "abc-123"
}
MIT