Hono
Add Nyoxis threat detection to a Hono application using composable middleware. Works on Node.js, Deno, Bun, and edge runtimes.
Prerequisites
- Hono 3 or later
- A Nyoxis workspace API key — get one here
No extra install required
Hono uses the native fetch API. If you're running on Node.js 18, no additional dependencies are needed.
Middleware
Create src/middleware/nyoxis.ts:
typescript
import { createMiddleware } from "hono/factory";
import type { Context } from "hono";
const NYO_API = "https://api.nyoxis.com";
type NyoxisOptions = {
apiKey: string;
blockOnHigh?: boolean;
onError?: "open" | "closed";
};
export function nyoxisWAF(options: NyoxisOptions) {
const { apiKey, blockOnHigh = false, onError = "open" } = options;
if (!apiKey) throw new Error("nyoxis: apiKey is required");
return createMiddleware(async (c: Context, next) => {
const url = new URL(c.req.url);
const payload = {
method: c.req.method,
path: url.pathname,
query: url.search ? url.search.slice(1) : undefined,
headers: Object.fromEntries(c.req.raw.headers.entries()),
ip_addr:
c.req.header("cf-connecting-ip") ??
c.req.header("x-real-ip") ??
c.env?.remoteAddr ??
undefined,
};
try {
const response = await fetch(`${NYO_API}/v0/predict?api_key=${apiKey}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(3000),
});
const verdict = await response.json();
// Store in Hono context variables for downstream handlers
c.set("nyoxis", verdict);
if (blockOnHigh && verdict.prediction?.risk === "high") {
return c.json({ error: "Forbidden" }, 403);
}
} catch (err) {
console.warn("[nyoxis] prediction error:", (err as Error).message);
if (onError === "closed") {
return c.json({ error: "Service unavailable" }, 503);
}
}
await next();
});
}Register the middleware
typescript
// src/index.ts
import { Hono } from "hono";
import { nyoxisWAF } from "./middleware/nyoxis";
// Declare the context variable type in your Hono app
type Variables = { nyoxis: Record<string, unknown> };
const app = new Hono<{ Variables: Variables }>();
// Apply globally
app.use(
"*",
nyoxisWAF({ apiKey: process.env.NYOXIS_API_KEY!, blockOnHigh: true }),
);
app.get("/", (c) => {
const verdict = c.get("nyoxis") ?? {};
const risk = (verdict.prediction as any)?.risk ?? "unknown";
return c.json({ ok: true, risk });
});
export default app;Apply to a specific route group
typescript
// Protect only /api/* routes
app.use("/api/*", nyoxisWAF({ apiKey: process.env.NYOXIS_API_KEY! }));Acting on the verdict
typescript
app.get("/sensitive", (c) => {
const verdict = c.get("nyoxis") ?? {};
const prediction = (verdict.prediction as any) ?? {};
if (["medium", "high"].includes(prediction.risk)) {
return c.json({ error: "Forbidden" }, 403);
}
const hasXss = prediction.attacks?.some(
(a: { kind: string }) => a.kind === "xss",
);
if (hasXss) {
console.warn("[security] XSS signal", {
path: new URL(c.req.url).pathname,
});
}
return c.json({ data: "sensitive content" });
});Edge runtime note
This middleware uses fetch and AbortSignal.timeout, both available on Cloudflare Workers, Deno Deploy, and Vercel Edge. No runtime-specific code is needed.
Next steps
- API Reference — complete field descriptions and status codes.
- Overview — how the classifier and redaction pipeline work.