How to Secure Your Fastify App with SecureNow — Real-Time API Protection via OpenTelemetry

Step-by-step guide to adding security monitoring to your Fastify API with the securenow npm package. Covers CLI setup, instrumentation, body capture caveats, PM2, Docker, and dashboard verification.

Lhoussine
Mar 26, 2026·8 min read
getting started securenow fastify

How to Secure Your Fastify App with SecureNow — Real-Time API Protection via OpenTelemetry

Fastify is built for speed — but speed without visibility is a liability. Once your Fastify API is handling production traffic, you need to know who is calling your endpoints, whether those requests look legitimate, and what patterns are emerging across your surface area.

SecureNow hooks into Fastify at the Node.js runtime level to capture every HTTP request as an OpenTelemetry trace. Those traces feed into a security analysis engine that detects SQL injection, credential stuffing, API abuse, and anomalous traffic — without adding middleware, plugins, or route changes to your Fastify app.

This guide covers the entire flow: installation, CLI authentication, app creation on the free trial, instrumentation, the important body-capture caveat, and production deployment with PM2 and Docker.

...

Prerequisites

  • Node.js 18+ installed
  • An existing Fastify project (or willingness to scaffold one)
  • 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. Fastify itself should already be in your project — SecureNow does not install or depend on it.

...

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. Once authenticated, the token is saved to ~/.securenow/credentials.json and every subsequent CLI command is authorized.

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-fastify-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 you will see:

✔ Application created

  SECURENOW_APPID=d4e5f6a7-b8c9-0123-4567-89abcdef0123
  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 d4e5f6a7-b8c9-0123-4567-89abcdef0123
...

Step 4: Configure Environment Variables

Create (or update) a .env file in your project root:

SECURENOW_APPID=d4e5f6a7-b8c9-0123-4567-89abcdef0123
SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
SECURENOW_LOGGING_ENABLED=1
SECURENOW_CAPTURE_BODY=0
VariablePurpose
SECURENOW_APPIDIdentifies your app in the dashboard. Use the key from Step 3.
SECURENOW_INSTANCEOTLP collector URL. Free trial default shown above.
SECURENOW_LOGGING_ENABLEDSet to 1 to forward console.log/warn/error as OTel logs.
SECURENOW_CAPTURE_BODYMust be 0 for Fastify. See the warning below.

⚠️ Body Capture Is Not Compatible with Fastify. Fastify's streaming request parser conflicts with body capture. Setting SECURENOW_CAPTURE_BODY=1 will cause requests with bodies to fail or return empty payloads. Always keep this set to 0 (or omit it entirely) when running Fastify. Headers, query parameters, route paths, status codes, and timing are all still captured — only raw body content is excluded.

...

Step 5: Instrument Your Fastify App

You have two options — pick whichever fits your workflow.

Option A: Require at the Top of Your Entry File (Recommended)

Add require('securenow/register') as the very first line of your entry file, before any other imports:

require('securenow/register');
require('securenow/console-instrumentation');

const Fastify = require('fastify');
const app = Fastify({ logger: true });

app.get('/health', async (request, reply) => {
  return { status: 'ok', timestamp: new Date().toISOString() };
});

app.post('/tasks', {
  schema: {
    body: {
      type: 'object',
      required: ['title'],
      properties: {
        title: { type: 'string' },
        description: { type: 'string' },
        priority: { type: 'string', enum: ['low', 'medium', 'high'] }
      }
    }
  }
}, async (request, reply) => {
  const task = { id: Date.now(), ...request.body, createdAt: new Date().toISOString() };
  reply.code(201).send(task);
});

const start = async () => {
  const PORT = process.env.PORT || 3000;
  await app.listen({ port: PORT, host: '0.0.0.0' });
};

start();

securenow/register starts the OpenTelemetry SDK, reads your .env, and auto-instruments HTTP, Fastify, database drivers, and more. securenow/console-instrumentation forwards console.* calls as OTel log records.

Option B: Zero Code Changes with the Preload Flag

If you prefer not to touch your source code, use Node's -r flag:

node -r securenow/register -r securenow/console-instrumentation app.js

Or in your package.json:

{
  "scripts": {
    "start": "node app.js",
    "start:observe": "node -r securenow/register -r securenow/console-instrumentation app.js"
  }
}
...

Step 6: Start and Verify

Run your app:

node app.js

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
{"level":30,"msg":"Server listening at http://0.0.0.0:3000"}

Generate some traffic:

curl http://localhost:3000/health
curl -X POST http://localhost:3000/tasks -H "Content-Type: application/json" -d '{"title":"Test task","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:

CommandWhat It Does
securenow tracesList recent traces
securenow traces show <traceId>Inspect a single trace
securenow traces analyze <traceId>AI-powered trace analysis
securenow logsList recent logs
securenow issuesView detected security issues
securenow analyticsTraffic and performance analytics
securenow ip <address>Look up an IP address
securenow blocklist add <ip>Block a malicious IP
securenow alerts rulesManage alert rules
securenow forensicsRun natural-language forensic queries
...

Production Deployment with PM2

For production, use PM2 with an ecosystem config:

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'my-fastify-api',
    script: './app.js',
    instances: 4,
    exec_mode: 'cluster',
    node_args: '-r securenow/register -r securenow/console-instrumentation',
    env: {
      SECURENOW_APPID: 'd4e5f6a7-b8c9-0123-4567-89abcdef0123',
      SECURENOW_INSTANCE: 'https://freetrial.securenow.ai:4318',
      SECURENOW_LOGGING_ENABLED: '1',
      SECURENOW_CAPTURE_BODY: '0',
      SECURENOW_NO_UUID: '1',
      NODE_ENV: 'production',
    }
  }]
};
pm2 start ecosystem.config.js

Setting SECURENOW_NO_UUID=1 ensures all cluster workers report under the same service name. Remember to keep SECURENOW_CAPTURE_BODY=0 in production as well.

...

Docker Deployment

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENV SECURENOW_APPID=my-fastify-api
ENV SECURENOW_INSTANCE=https://freetrial.securenow.ai:4318
ENV SECURENOW_LOGGING_ENABLED=1
ENV SECURENOW_CAPTURE_BODY=0
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "app.js"]

If you used Option B (preload flag), update the CMD:

CMD ["node", "-r", "securenow/register", "-r", "securenow/console-instrumentation", "app.js"]
...

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

Even without body capture, SecureNow analyzes URL paths, query strings, headers, response codes, and timing — more than enough to catch the vast majority of attacks targeting a Fastify API.

...

Recap

StepCommand / ActionTime
Installnpm install securenow10 s
Loginnpx securenow login20 s
Create appnpx securenow apps create my-fastify-api15 s
ConfigureAdd env vars to .env (body capture OFF)30 s
InstrumentAdd require('securenow/register') or use -r flag30 s
Verifynpx securenow status10 s

Six steps, under five minutes, zero plugin changes. Your Fastify API is now observable and protected.

...

Next Steps

Happy shipping — and happy securing.

Frequently Asked Questions

Why can't I enable request body capture with Fastify?

Fastify uses a streaming parser that consumes the request body before middleware can clone it. Setting SECURENOW_CAPTURE_BODY=1 causes a conflict where the body stream is read twice, resulting in empty or errored payloads. Always set SECURENOW_CAPTURE_BODY=0 (or omit it entirely) when using Fastify.

Do I need to install any Fastify plugin for SecureNow?

No. SecureNow uses the Node.js -r preload flag or a require() call to auto-instrument your application at the runtime level. No Fastify plugins, hooks, or decorators are needed.

Does SecureNow work with Fastify's built-in schema validation?

Yes. SecureNow operates at the HTTP transport layer and captures requests before Fastify's schema validation runs. Invalid requests that fail validation are still traced, which is valuable for detecting injection attempts and fuzzing.

Can I use SecureNow with Fastify in cluster mode behind PM2?

Yes. Use node_args in your PM2 ecosystem config to preload securenow/register and set SECURENOW_NO_UUID=1 so all workers report under the same service name in the dashboard.

Recommended reading

Getting Started with SecureNow and Nuxt 3 — Add Security Monitoring in Under 2 Minutes

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 2
One Flag to Trace Them All — `-r securenow/register` Now Works for ESM and CJS

Stop 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 2
Add Security Monitoring to a Next.js App with SecureNow — Traces, Logs, and Body Capture on AWS

Step-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
deploy nextjs hacker news aws securenow