NestJS

Monitor a NestJS API with the in-process SDK, using a global middleware for requests and your auth service for sign-in events.

On this page

NestJS runs on Express (or Fastify), so it uses the SDK and reports after the response.

What you'll have

  • Every request checked with its status.
  • Sign-ins, admin grants, new keys and LLM spend reported from your services.
  • New outbound hosts noticed, and dependencies checked in CI.

Before you start

Do steps 1 and 2 of the general cookbook first: App monitoring is on, the app is added, and its beam_app_ token is in your secret store as BEAM_APP_TOKEN. Everything below needs @agent-beam/beam 0.2.32 or newer.

Install and provide one monitor

Code
npm install @agent-beam/beam
Code
// beam.ts
import { createAppMonitor } from "@agent-beam/beam/monitor";

export const beam = createAppMonitor({ stack: "nestjs", watchOutbound: true });

Report each request after the response

Code
// beam.middleware.ts
import { Injectable, NestMiddleware } from "@nestjs/common";
import type { Request, Response, NextFunction } from "express";
import { beam } from "./beam";

@Injectable()
export class BeamMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const started = Date.now();
    res.on("finish", () =>
      beam.request({
        method: req.method,
        url: req.originalUrl,
        ip: req.ip,
        userAgent: req.get("user-agent"),
        status: res.statusCode,
        durationMs: Date.now() - started,
        headers: req.headers,
      }),
    );
    next();
  }
}
Code
// app.module.ts
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(BeamMiddleware).forRoutes("*");
  }
}

In main.ts, set app.set("trust proxy", 1) (Express adapter) so req.ip is the client behind your load balancer.

Pause attackers

To also refuse senders the workspace paused (scanners, password guessers, LLM abusers), replace BeamMiddleware with the SDK's own middleware in main.ts. It answers a paused sender with a 429 and Retry-After, and reports every other request after its response, with the status:

Code
// main.ts
const app = await NestFactory.create(AppModule);
app.set("trust proxy", 1);
app.use(beam.middleware()); // before your routes

Nothing is refused until an admin turns pausing on under Settings > Pausing (it starts in dry run) or pauses someone. With trust proxy set to your proxy count, req.ip is the address your proxy added, which Beam can pause; Beam never pauses one the client could have written into X-Forwarded-For. Pausing needs @agent-beam/beam 0.2.35.

Report sign-ins, admins and keys

In your auth service, where you validate a user (for example a Passport LocalStrategy.validate):

Code
async validate(username: string, password: string) {
  const user = await this.users.verify(username, password);
  if (!user) {
    beam.event("login.failed", { user: await this.users.idFor(username) });
    throw new UnauthorizedException();
  }
  beam.event("login.succeeded", { user: user.id });
  return user;
}

Add ip from the request where you have it. Call beam.event("admin.granted", ...) and beam.event("apikey.created", ...) in the services that change roles and keys, and beam.usage() after each model call.

Prove it works

Code
beam monitor status   # app, level, stack, usage
beam monitor test     # a test finding through your alert workflows
curl -s "https://your-app.example.com/?id=1%20UNION%20SELECT%201" -o /dev/null

Within a minute, the SQL injection test shows on App monitoring: under Needs action if the app answered 2xx, or counted under "needed nothing from you" if it refused it. Then sign in to a test account with a wrong password ten times and the right one once: Sign-in succeeded after 10 failed attempts appears as critical.

Check dependencies on every pull request

Code
# .github/workflows/beam-cve.yml
name: Dependency check
on: [pull_request]
jobs:
  cve:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx -y @agent-beam/beam cve check . --fail-on high
        env:
          BEAM_APP_TOKEN: ${{ secrets.BEAM_APP_TOKEN }}

What Beam watches for on this stack

Besides every built-in rule and the attack-in-progress signals:

RuleMeansSeverity
Node.js package files served.npmrc, package-lock.json or node_modules answered 2xxHigh
GraphQL introspectionA __schema query in a GET, if you use @nestjs/graphqlMedium

Attacks on other frameworks (WordPress probes, Laravel or Spring exploits, and so on) are rolled up as Not your stack when there's no status to say otherwise, and never alert.

Close what Beam would catch

Beam raises these when they're found; better that there's nothing to find:

  • Turn off GraphQL introspection and the playground in production unless they're meant to be public.
  • Don't serve Swagger (SwaggerModule.setup) publicly in production, or put it behind auth.
  • Use a global ValidationPipe with whitelist: true.

Coverage checklist

FeatureDone when
Middleware on every routeFindings show Answered or Refused
Stack declaredstack: "nestjs"
Sign-in eventslogin.failed and login.succeeded in the auth service
Admin, key and LLM eventsReported where they happen
Dependency checksThe CI job runs on pull requests
AlertsCritical pages someone, high reaches a channel (step 8)
Pausingbeam.middleware() in main.ts, and Settings > Pausing on after a dry run
RoutineSomeone reads Needs action daily and Attack analytics weekly

Set this up with your coding agent

Install the agentbeam-setup skill for Claude Code, Codex, Cursor, Gemini CLI, OpenCode and Copilot, then ask your agent to set up AgentBeam on this machine. Beam scans the skill before it installs.

npx -y @agent-beam/beam add skill agentbeam-setup -g --all-agents
Docs
Esc

Jump to a section

Actions