HTTP with Fastify
One concrete BYO HTTP recipe: agent SSE, interruptible graph, and durable Postgres adapters. Fastify is the sample server — the same patterns drop into Hono, Nest, or plain Node.
1. Install
Install from npm (@monorch/ai@0.2.0). Native binaries for @monorch/runtime ship as optional platform packages — see platforms.
terminal
bash
pnpm add @monorch/ai
# optional: pnpm add pg # only for @monorch/ai/postgres2. Agent + SSE
support.ts
typescript
import Fastify from "fastify";
import { agent, tool } from "@monorch/ai";
import { openai, mock } from "@monorch/ai/openai";
import { z } from "zod";
const add = tool({
name: "add",
input: z.object({ a: z.number(), b: z.number() }),
permission: { type: "roles", roles: ["agent"] },
execute: ({ a, b }) => ({ sum: a + b }),
});
const bot = agent({
name: "math",
model: process.env.OPENAI_API_KEY
? openai("gpt-4.1-mini")
: mock([
{ toolCalls: [{ id: "1", name: "add", arguments: { a: 2, b: 3 } }] },
{ text: "2 + 3 = 5" },
]),
tools: [add],
instructions: "Use tools for math.",
});
const app = Fastify();
app.post("/support/stream", async (req, reply) => {
const message = (req.body as { message?: string })?.message ?? "2+3";
reply.hijack();
reply.raw.writeHead(200, {
"content-type": "text/event-stream",
"cache-control": "no-cache",
connection: "keep-alive",
});
for await (const ev of bot.stream(message)) {
reply.raw.write(`data: ${JSON.stringify(ev)}\n\n`);
}
reply.raw.end();
});3. Interrupt + resume
refund.ts
typescript
import { graph, memorySaver } from "@monorch/ai";
const checkpointer = memorySaver();
const refund = graph("refund")
.node("lookup", async ({ input }) => ({
output: `order:${input.orderId}`,
state: { orderId: input.orderId },
}))
.interrupt("approve", { prompt: "Approve refund?" })
.node("pay", async ({ outputs }) => `refunded:${outputs.lookup}`)
.compile({ checkpointer });
app.post("/refund", async (req) => {
const body = req.body as { orderId?: string; threadId?: string };
const threadId = body.threadId ?? `refund-${Date.now()}`;
const run = await refund.start(
{ orderId: body.orderId ?? "ord_1" },
{ threadId },
);
return { id: run.id, threadId, status: run.status };
});
app.post("/refund/:threadId/resume", async (req) => {
const run = await refund.restore((req.params as { threadId: string }).threadId);
const resumed = await run.resume("approved");
return { id: resumed.id, status: resumed.status, outputs: resumed.outputs };
});4. Postgres (optional)
Swap in-memory helpers for durable adapters. pg is an optional peer.
postgres.ts
typescript
import pg from "pg";
import {
ensureMonorchSchema,
postgresCheckpointer,
postgresThreads,
} from "@monorch/ai/postgres";
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
await ensureMonorchSchema(pool);
const checkpointer = postgresCheckpointer(pool);
const threads = postgresThreads(pool);
// graph compile({ checkpointer })
// agent.run(msg, { threadId: "t1", memory: threads })5. Smoke
terminal
bash
pnpm smoke
# optional live provider:
# LIVE_SMOKE=1 OPENAI_API_KEY=... pnpm smoke:liveThe repo smoke at examples/fastify covers handoffs, MCP, OTel, branching, abort, hot-reload, and Postgres stand-ins — Fastify is only the sample host.