How to Secure Your NestJS App with SecureNow — TypeScript-Native Tracing and Threat Detection
End-to-end guide to adding security monitoring to your NestJS application using the securenow npm package. Covers instrument.js setup, TypeScript compatibility, PM2, Docker, and dashboard verification.

How to Secure Your NestJS App with SecureNow — TypeScript-Native Tracing and Threat Detection
NestJS gives you a structured, TypeScript-first framework for building scalable server-side applications. But structure alone does not tell you who is calling your API, whether those requests are malicious, or what attack patterns are emerging across your endpoints.
SecureNow captures every HTTP request flowing through your NestJS app as an OpenTelemetry trace. Those traces feed a security analysis engine that detects SQL injection, credential stuffing, API abuse, and anomalous traffic — without modifying your controllers, guards, interceptors, or module definitions.
Because NestJS is TypeScript-based, the instrumentation approach differs slightly from plain JavaScript frameworks: you create a small instrument.js file and preload it before your app starts. This guide covers the full setup, from installation to production deployment.
Prerequisites
- Node.js 18+ installed
- An existing NestJS project (generated via
@nestjs/clior equivalent) ts-nodeavailable (included with@nestjs/cliprojects by default)- A terminal and a browser
No SecureNow account yet? The CLI handles signup for you.
Step 1: Install the Package
From your project directory:
npm install securenow
This single package includes the OpenTelemetry SDK, auto-instrumentations for Node.js, an OTLP exporter, the SecureNow CLI, and optional console-log forwarding. Your NestJS dependencies (@nestjs/core, @nestjs/common, etc.) should already be in your project.
Step 2: Log In via the CLI
Authenticate with SecureNow:
npx securenow login
A browser tab opens at app.securenow.ai where you can sign up or log in. The token is saved to ~/.securenow/credentials.json and authorizes all subsequent CLI commands.
Prefer a non-interactive flow? Generate a CLI token from your dashboard at Settings → CLI Token, then run:
npx securenow login --token YOUR_TOKEN
Verify you are logged in:
npx securenow whoami
Step 3: Create an Application (Free Trial)
Every monitored application gets a unique identifier (the app key). Create one:
npx securenow apps create my-nestjs-api
When prompted to pick a ClickHouse instance, choose Free Trial — this gives you a managed OTLP collector at https://freetrial.securenow.ai:4318 at no cost and with no credit card.
After creation:
✔ Application created
SECURENOW_APPID=e5f6a7b8-c9d0-1234-5678-9abcdef01234
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
Add these to your .env file.
Set the new app as your default:
npx securenow config set defaultApp e5f6a7b8-c9d0-1234-5678-9abcdef01234
Step 4: Configure Environment Variables
Create (or update) a .env file in your project root:
SECURENOW_APPID=e5f6a7b8-c9d0-1234-5678-9abcdef01234
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
SECURENOW_LOGGING_ENABLED=1
SECURENOW_CAPTURE_BODY=1
| Variable | Purpose |
|---|---|
SECURENOW_APPID | Identifies your app in the dashboard. Use the key from Step 3. |
SECURENOW_INSTANCE | OTLP collector URL. Free trial default shown above. |
SECURENOW_LOGGING_ENABLED | Set to 1 to forward console.log/warn/error as OTel logs. |
SECURENOW_CAPTURE_BODY | Set to 1 to attach request bodies to trace spans. NestJS supports body capture fully. Sensitive fields are automatically redacted. |
Step 5: Create the Instrumentation File
This is the key step that differs from plain JavaScript frameworks. Create a file called instrument.js in your project root (the same directory as package.json and tsconfig.json):
// instrument.js
require('securenow/register');
require('securenow/console-instrumentation');
That is the entire file — two lines. It must be plain JavaScript (not TypeScript) because it is loaded before ts-node or the compiled output.
Important: Do not modify
src/main.ts. Your NestJS entry file stays exactly as-is. Theinstrument.jsfile is preloaded via the-rflag, so the OpenTelemetry SDK initializes beforeNestFactory.create()runs.
Step 6: Update Your Package Scripts
Update your package.json scripts to preload instrument.js in both development and production modes:
{
"scripts": {
"start:dev": "node -r ./instrument.js -r ts-node/register src/main.ts",
"start": "node -r ./instrument.js dist/main.js",
"build": "nest build"
}
}
| Script | What It Does |
|---|---|
start:dev | Loads instrument.js, then ts-node/register for TypeScript compilation, then your NestJS entry file. Used during development. |
start | Loads instrument.js, then runs the compiled JavaScript output. Used in production after nest build. |
build | Standard NestJS build — compiles TypeScript to dist/. No changes needed. |
Your src/main.ts remains untouched. Here is a typical NestJS entry file for reference:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT || 3000);
}
bootstrap();
Step 7: Example NestJS Application
Here is a minimal controller and module to illustrate what gets traced:
// src/app.controller.ts
import { Controller, Get, Post, Body, HttpCode } from '@nestjs/common';
interface CreateTaskDto {
title: string;
description?: string;
priority?: 'low' | 'medium' | 'high';
}
@Controller()
export class AppController {
@Get('health')
getHealth() {
return { status: 'ok', timestamp: new Date().toISOString() };
}
@Post('tasks')
@HttpCode(201)
createTask(@Body() body: CreateTaskDto) {
return {
id: Date.now(),
...body,
createdAt: new Date().toISOString(),
};
}
}
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
@Module({
controllers: [AppController],
})
export class AppModule {}
SecureNow auto-instruments every route registered through NestJS controllers. The @Get, @Post, @Put, @Delete decorators all produce properly named trace spans.
Step 8: Start and Verify
Run in development mode:
npm run start:dev
You should see confirmation in your terminal:
[securenow] OTel SDK started → https://freetrial.securenow.ai:4318/v1/traces
[securenow] 📋 Logging: ENABLED → https://freetrial.securenow.ai:4318/v1/logs
[securenow] Console instrumentation installed
[Nest] LOG [NestFactory] Starting Nest application...
[Nest] LOG [NestApplication] Nest application successfully started
Generate some traffic:
curl http://localhost:3000/health
curl -X POST http://localhost:3000/tasks -H "Content-Type: application/json" -d '{"title":"Deploy to staging","priority":"high"}'
Then check your dashboard:
npx securenow status
Your app should appear as protected. Browse traces from the terminal:
npx securenow traces
Or open app.securenow.ai to explore traces, logs, security issues, and analytics.
Bonus: Useful CLI Commands
Once your app is instrumented, the CLI becomes your terminal-based control plane:
| Command | What It Does |
|---|---|
securenow traces | List recent traces |
securenow traces show <traceId> | Inspect a single trace |
securenow traces analyze <traceId> | AI-powered trace analysis |
securenow logs | List recent logs |
securenow issues | View detected security issues |
securenow analytics | Traffic and performance analytics |
securenow ip <address> | Look up an IP address |
securenow blocklist add <ip> | Block a malicious IP |
securenow alerts rules | Manage alert rules |
securenow forensics | Run natural-language forensic queries |
Production Deployment with PM2
Build your NestJS project first, then deploy with PM2:
npm run build
// ecosystem.config.js
module.exports = {
apps: [{
name: 'my-nestjs-api',
script: 'dist/main.js',
instances: 4,
exec_mode: 'cluster',
node_args: '-r ./instrument.js',
env: {
SECURENOW_APPID: 'e5f6a7b8-c9d0-1234-5678-9abcdef01234',
SECURENOW_INSTANCE: 'https://freetrial.securenow.ai:4318',
SECURENOW_LOGGING_ENABLED: '1',
SECURENOW_CAPTURE_BODY: '1',
SECURENOW_NO_UUID: '1',
NODE_ENV: 'production',
}
}]
};
pm2 start ecosystem.config.js
The PM2 config points script to dist/main.js (the compiled output) and uses node_args to preload instrument.js. Setting SECURENOW_NO_UUID=1 ensures all cluster workers report under the same service name.
Docker Deployment
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
ENV SECURENOW_APPID=my-nestjs-api
ENV SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
ENV SECURENOW_LOGGING_ENABLED=1
ENV SECURENOW_CAPTURE_BODY=1
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "-r", "./instrument.js", "dist/main.js"]
The CMD preloads instrument.js before running the compiled NestJS output. Make sure instrument.js is in the Docker build context (it lives at the project root alongside package.json).
What SecureNow Detects Automatically
Once traces are flowing, SecureNow watches for:
- SQL injection — malicious patterns in query parameters and request bodies
- XSS attempts — script injection in user input
- Credential stuffing — high-velocity failed authentication attempts
- API abuse — unusual request patterns, rate-limit evasion, unauthorized endpoint access
- Anomalous traffic — AI-powered detection of behavioral outliers
- Supply-chain signals — unexpected outbound calls from your service
- Performance degradation — slow queries, high error rates, latency spikes
NestJS guards that reject requests still generate traces, so SecureNow can detect patterns like repeated 403 Forbidden responses from a single IP — a strong signal for brute-force or enumeration attacks.
Recap
| Step | Command / Action | Time |
|---|---|---|
| Install | npm install securenow | 10 s |
| Login | npx securenow login | 20 s |
| Create app | npx securenow apps create my-nestjs-api | 15 s |
| Configure | Add env vars to .env | 30 s |
| Create instrument.js | Two-line file at project root | 15 s |
| Update scripts | Add -r ./instrument.js to start commands | 30 s |
| Verify | npm run start:dev → npx securenow status | 20 s |
Seven steps, under five minutes, zero changes to your NestJS source code. Your application is now observable and protected.
Next Steps
- Explore the SecureNow dashboard to view traces, logs, and security issues
- Set up alert rules for critical security events
- Run
npx securenow forensicsto ask natural-language questions about your traffic - Read the full environment variable reference for advanced tuning
Happy shipping — and happy securing.
Frequently Asked Questions
Why do I need a separate instrument.js file for NestJS?
NestJS uses TypeScript and compiles via ts-node (dev) or tsc (production). OpenTelemetry instrumentation must be loaded before any application code is imported. A plain JavaScript instrument.js file preloaded with -r ./instrument.js ensures the SDK initializes before TypeScript compilation and NestJS bootstrap begin.
Does SecureNow work with NestJS guards, interceptors, and pipes?
Yes. SecureNow instruments at the HTTP transport layer, so guards, interceptors, pipes, and exception filters all execute normally. Requests blocked by guards still generate traces — useful for detecting unauthorized access attempts.
Can I use the same setup for development and production?
The instrument.js file is identical for both. In development you run node -r ./instrument.js -r ts-node/register src/main.ts. In production, after running nest build, you run node -r ./instrument.js dist/main.js. The preload flag is the same — only the entry file changes.
Do I need to modify src/main.ts at all?
No. Your src/main.ts stays completely unchanged. The instrument.js file is loaded before main.ts via the -r flag, so the OpenTelemetry SDK is initialized before NestFactory.create() is called. No imports, decorators, or module changes are needed in your NestJS source code.
Recommended reading
A hands-on walkthrough for adding security observability to a Nuxt 3 app using the securenow npm package and official Nuxt module. Covers installation, nuxt.config.ts setup, environment variables, optional tuning, deployment targets, CLI verification, and troubleshooting.
Apr 2Stop juggling --require and --import flags. securenow/register now auto-registers the ESM loader hook via module.register() on Node >=20.6, so a single -r flag is all you need for both CommonJS and ESM apps.
Apr 2Step-by-step guide to integrating SecureNow into a self-hosted Next.js application on AWS EC2. Covers installation, instrumentation, environment configuration, verifying traces and logs, enabling request body capture, and creating alert rules.
Mar 29