tutorial

Monitoring Your NestJS API with Vigilmon: Health Checks & Alerts

Add production-grade uptime monitoring to your NestJS API in under 20 minutes — health checks with TerminusModule, HTTP monitors, and Slack webhook alerts.

Monitoring Your NestJS API with Vigilmon: Health Checks & Alerts

Your NestJS API went down. Your users are getting 503s. You found out 45 minutes later when someone posted in your Discord.

Most NestJS tutorials cover building the API. This one covers what happens after you deploy it. By the end you'll have:

  • A /health endpoint powered by @nestjs/terminus
  • HTTP uptime monitoring with Vigilmon
  • Slack webhook alerts when your API goes down
  • A status badge you can embed anywhere

The whole setup takes about 20 minutes and runs free on Vigilmon's starter plan.


Step 1: Add health checks with TerminusModule

NestJS ships with @nestjs/terminus — a first-class health check module that integrates cleanly with the DI system and supports all the dependency checks you care about: database, Redis, HTTP, disk, memory.

Install the dependencies:

npm install @nestjs/terminus @nestjs/axios axios

Create a HealthModule:

// src/health/health.module.ts
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { HttpModule } from '@nestjs/axios';
import { HealthController } from './health.controller';

@Module({
  imports: [TerminusModule, HttpModule],
  controllers: [HealthController],
})
export class HealthModule {}

Add the controller with checks for your actual dependencies:

// src/health/health.controller.ts
import { Controller, Get } from '@nestjs/common';
import {
  HealthCheck,
  HealthCheckService,
  HttpHealthIndicator,
  TypeOrmHealthIndicator,
  MemoryHealthIndicator,
  DiskHealthIndicator,
} from '@nestjs/terminus';

@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private http: HttpHealthIndicator,
    private db: TypeOrmHealthIndicator,
    private memory: MemoryHealthIndicator,
    private disk: DiskHealthIndicator,
  ) {}

  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      // Database connectivity
      () => this.db.pingCheck('database'),
      // Heap usage stays under 300MB
      () => this.memory.checkHeap('memory_heap', 300 * 1024 * 1024),
      // Disk usage stays under 90%
      () =>
        this.disk.checkStorage('storage', {
          path: '/',
          thresholdPercent: 0.9,
        }),
    ]);
  }
}

Register HealthModule in your AppModule:

// src/app.module.ts
import { HealthModule } from './health/health.module';

@Module({
  imports: [
    // ... your other modules
    HealthModule,
  ],
})
export class AppModule {}

Verify it locally:

npm run start:dev
curl http://localhost:3000/health

A healthy response looks like:

{
  "status": "ok",
  "info": {
    "database": { "status": "up" },
    "memory_heap": { "status": "up" },
    "storage": { "status": "up" }
  },
  "details": { ... }
}

When any check fails, Terminus returns a 503 with details about exactly which component is unhealthy. That's what you want — a monitoring tool that can react to the specific failure, not just "something is wrong."


Step 2: Set up HTTP monitoring in Vigilmon

With your health endpoint live, connect it to Vigilmon:

  1. Sign up at vigilmon.online (free, no card required)
  2. Click New Monitor → HTTP
  3. Enter https://api.yourdomain.com/health
  4. Set check interval: 1 minute (paid) or 5 minutes (free)
  5. Under Expected status code, set 200
  6. Save

Vigilmon checks from multiple regions. If your endpoint returns a non-200 or times out, you get alerted within the check interval.

You can also add monitors for individual critical routes:

https://api.yourdomain.com/         → root alive
https://api.yourdomain.com/health   → full health check
https://api.yourdomain.com/v1/ping  → versioned API alive

Each monitor is independent. A timeout on /v1/ping doesn't mask a 500 on /health.


Step 3: Slack webhook alerts

Go to Notifications → New Channel → Slack in Vigilmon and paste your Slack incoming webhook URL.

If you don't have a webhook yet:

  1. Go to api.slack.com/appsCreate New App → From scratch
  2. Under Incoming Webhooks, toggle on and click Add New Webhook to Workspace
  3. Pick a channel and copy the webhook URL

Back in Vigilmon, enable the Slack channel on your monitors. When your NestJS API goes down, you'll see something like this in Slack within minutes:

🔴 DOWN: api.yourdomain.com/health
Status: 503 Service Unavailable
Region: US-East
2 minutes ago

And when it recovers:

✅ RECOVERED: api.yourdomain.com/health
Downtime: 8 minutes

You can add multiple channels — Slack for on-call, email for the team digest.


Step 4: Heartbeat monitoring for background jobs

HTTP monitoring catches API outages. But what about your NestJS scheduled tasks?

NestJS uses @nestjs/schedule for cron jobs. If a scheduled task stops running silently, your HTTP endpoint stays green — but work stops getting done.

The heartbeat pattern: your task pings a unique URL at the end of every successful run. If Vigilmon doesn't receive a ping within the expected window, it alerts you.

First, enable schedules:

npm install @nestjs/schedule
// src/app.module.ts
import { ScheduleModule } from '@nestjs/schedule';

@Module({
  imports: [
    ScheduleModule.forRoot(),
    // ...
  ],
})
export class AppModule {}

Add a heartbeat ping to any scheduled task:

// src/tasks/digest.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { firstValueFrom } from 'rxjs';

@Injectable()
export class DigestService {
  private readonly logger = new Logger(DigestService.name);

  constructor(
    private readonly http: HttpService,
    private readonly config: ConfigService,
  ) {}

  @Cron(CronExpression.EVERY_DAY_AT_6AM)
  async sendDailyDigest() {
    try {
      await this.generateAndSendDigest();

      // Ping heartbeat on success
      const heartbeatUrl = this.config.get<string>('VIGILMON_DIGEST_HEARTBEAT');
      if (heartbeatUrl) {
        await firstValueFrom(this.http.get(heartbeatUrl));
      }
    } catch (err) {
      this.logger.error('Daily digest failed', err);
      // No ping sent → Vigilmon alerts on missed heartbeat
    }
  }

  private async generateAndSendDigest() {
    // your logic here
  }
}

In Vigilmon, create a Heartbeat Monitor:

  1. Click New Monitor → Heartbeat
  2. Set expected interval to 24 hours
  3. Copy the ping URL
  4. Set VIGILMON_DIGEST_HEARTBEAT=<url> in your .env

Now if the job crashes, throws, or stops running entirely, Vigilmon will alert you within one interval of the missed ping.


Step 5: Public status page and badge

A status page answers "is the API down for everyone?" before users start filing issues.

In Vigilmon:

  1. Go to Status Pages → New Status Page
  2. Add your monitors, give it a name
  3. Copy the public URL

Link it from your API docs, README, or error responses:

Service unavailable. Check our status page: https://status.yourdomain.com

Every monitor also has a live SVG badge at:

https://vigilmon.online/badge/{your-monitor-slug}

Add it to your README.md:

![API Status](https://vigilmon.online/badge/your-monitor-slug)

Or as an HTML embed with a link to your status page:

<a href="https://status.yourdomain.com">
  <img src="https://vigilmon.online/badge/your-monitor-slug" alt="API Status">
</a>

The badge shows current status and response time, live.


What you've built

| What | How | |------|-----| | Health endpoint | @nestjs/terminus + HealthController | | DB, memory, disk checks | TypeOrmHealthIndicator, MemoryHealthIndicator, DiskHealthIndicator | | HTTP uptime monitoring | Vigilmon HTTP monitor → /health | | Slack alerts | Vigilmon Slack notification channel | | Scheduled task monitoring | Heartbeat ping in @Cron handlers | | Status page | Vigilmon public status page | | README badge | /badge/{slug} SVG embed |

The whole stack is free to start. You'll know about outages before your users do.


Next steps

  • Add a HttpHealthIndicator check for any third-party APIs your NestJS app depends on
  • Monitor response time trends in Vigilmon — gradual slowdowns are often visible hours before a full outage
  • Add separate heartbeat monitors for every background job that matters (report generation, sync tasks, billing jobs)

Get started free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →