NestJS
Add Nyoxis threat detection to a NestJS application using a guard that calls /v0/predict before each request is handled.
Prerequisites
- Node.js 18 or later
- NestJS 10 or later
- A Nyoxis workspace API key — get one here
No extra install required
NestJS ships with @nestjs/axios (wraps axios). You can also use the native fetch API available in Node.js 18+, which this guide uses to keep dependencies minimal.
Guard
Create src/guards/nyoxis.guard.ts:
typescript
import {
CanActivate,
ExecutionContext,
Injectable,
Logger,
} from "@nestjs/common";
import { Request, Response } from "express";
const NYO_API = "https://api.nyoxis.com";
@Injectable()
export class NyoxisGuard implements CanActivate {
private readonly logger = new Logger(NyoxisGuard.name);
constructor(
private readonly apiKey: string,
private readonly options: {
blockOnHigh?: boolean;
onError?: "open" | "closed";
} = {},
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest<Request>();
const res = context.switchToHttp().getResponse<Response>();
const payload = {
method: req.method,
path: req.path,
query: Object.keys(req.query).length
? new URLSearchParams(req.query as Record<string, string>).toString()
: undefined,
headers: req.headers as Record<string, string>,
ip_addr: req.ip,
body:
typeof req.body === "string"
? req.body
: req.body
? JSON.stringify(req.body)
: undefined,
};
try {
const response = await fetch(
`${NYO_API}/v0/predict?api_key=${this.apiKey}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(3000),
},
);
const verdict = await response.json();
// Attach verdict to request for controller access
(req as any).nyoxis = verdict;
if (this.options.blockOnHigh && verdict.prediction?.risk === "high") {
res.status(403).json({ error: "Forbidden" });
return false;
}
return true;
} catch (err) {
this.logger.warn(`Prediction error: ${(err as Error).message}`);
if (this.options.onError === "closed") {
res.status(503).json({ error: "Service unavailable" });
return false;
}
// Fail open
return true;
}
}
}Register globally
typescript
// src/main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { NyoxisGuard } from "./guards/nyoxis.guard";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(
new NyoxisGuard(process.env.NYOXIS_API_KEY!, { blockOnHigh: true }),
);
await app.listen(3000);
}
bootstrap();Alternatively, apply per-controller or per-route:
typescript
// src/app.controller.ts
import { Controller, Get, Req, UseGuards } from "@nestjs/common";
import { NyoxisGuard } from "./guards/nyoxis.guard";
import { Request } from "express";
@Controller()
@UseGuards(new NyoxisGuard(process.env.NYOXIS_API_KEY!))
export class AppController {
@Get()
getRoot(@Req() req: Request) {
const risk = (req as any).nyoxis?.prediction?.risk ?? "unknown";
return { ok: true, risk };
}
}Acting on the verdict
typescript
import { Get, Req, ForbiddenException } from "@nestjs/common";
import { Request } from "express";
@Get("sensitive")
getSensitive(@Req() req: Request) {
const verdict = (req as any).nyoxis ?? {};
const prediction = verdict.prediction ?? {};
if (["medium", "high"].includes(prediction.risk)) {
throw new ForbiddenException("Blocked by WAF");
}
const sqli = prediction.attacks?.find(
(a: { kind: string; confidence: number }) =>
a.kind === "sql_injection" && a.confidence > 0.8,
);
if (sqli) {
console.warn("[security] SQL injection detected", {
path: req.path,
ip: req.ip,
});
}
return { data: "sensitive content" };
}Next steps
- API Reference — complete field descriptions and status codes.
- Overview — how the classifier and redaction pipeline work.