Koa

Add Nyoxis threat detection to a Koa application using a middleware function.

Prerequisites

  • Node.js 18 or later
  • Koa 2 or later
  • A Nyoxis workspace API key — get one here

No extra install required

Uses the native fetch API available in Node.js 18+.

Middleware

Create middleware/nyoxis.js:

javascript
const NYO_API = "https://api.nyoxis.com"; /** * Nyoxis WAF middleware for Koa. * * @param {object} options * @param {string} options.apiKey - Your workspace API token. * @param {boolean} [options.blockOnHigh=false] - Reject requests with risk "high". * @param {string} [options.onError="open"] - "open" | "closed" */ function nyoxisWAF({ apiKey, blockOnHigh = false, onError = "open" } = {}) { if (!apiKey) throw new Error("nyoxis: apiKey is required"); return async function nyoxisMiddleware(ctx, next) { const url = new URL(ctx.url, `http://${ctx.host}`); const payload = { method: ctx.method, path: ctx.path, query: url.search ? url.search.slice(1) : undefined, headers: ctx.headers, ip_addr: ctx.ip, body: typeof ctx.request.body === "string" ? ctx.request.body : ctx.request.body != null ? JSON.stringify(ctx.request.body) : 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(); // Attach verdict to Koa context state ctx.state.nyoxis = verdict; if (blockOnHigh && verdict.prediction?.risk === "high") { ctx.status = 403; ctx.body = { error: "Forbidden" }; return; } } catch (err) { console.warn("[nyoxis] prediction error:", err.message); if (onError === "closed") { ctx.status = 503; ctx.body = { error: "Service unavailable" }; return; } } await next(); }; } module.exports = { nyoxisWAF };

Register the middleware

javascript
const Koa = require("koa"); const Router = require("@koa/router"); const bodyParser = require("koa-bodyparser"); const { nyoxisWAF } = require("./middleware/nyoxis"); const app = new Koa(); const router = new Router(); app.use(bodyParser()); app.use(nyoxisWAF({ apiKey: process.env.NYOXIS_API_KEY, blockOnHigh: true })); router.get("/", (ctx) => { const risk = ctx.state.nyoxis?.prediction?.risk ?? "unknown"; ctx.body = { ok: true, risk }; }); app.use(router.routes()); app.listen(3000);

Acting on the verdict

javascript
router.get("/sensitive", (ctx) => { const verdict = ctx.state.nyoxis ?? {}; const prediction = verdict.prediction ?? {}; if (["medium", "high"].includes(prediction.risk)) { ctx.status = 403; ctx.body = { error: "Forbidden" }; return; } const xss = prediction.attacks?.find((a) => a.kind === "xss"); if (xss && xss.confidence > 0.8) { ctx.app.emit("security:alert", { kind: "xss", path: ctx.path, ip: ctx.ip, }); } ctx.body = { data: "sensitive content" }; });

TypeScript

For TypeScript projects, extend Koa's DefaultState to type the verdict:

typescript
import type Application from "koa"; import type Router from "@koa/router"; declare module "koa" { interface DefaultState { nyoxis?: { is_cached: boolean; prediction?: { risk: "none" | "low" | "medium" | "high"; risk_score: number; attacks: Array<{ kind: string; confidence: number }>; }; }; } }

Next steps

  • API Reference — complete field descriptions and status codes.
  • Overview — how the classifier and redaction pipeline work.

Cookie preferences

Nyoxis uses essential cookies for authentication and session security. We only enable Analytics after you consent. See our Cookie Policy for details.