DEV Community

Vigilmon
Vigilmon

Posted on

How to Monitor Your TypeScript API (ts-node, tsx, tsc) with Vigilmon

How to Monitor Your TypeScript API (ts-node, tsx, tsc) with Vigilmon

TypeScript APIs run on Node.js-but whether you're using ts-node in development, tsx for fast iteration, or compiled JavaScript in production, the monitoring approach is the same. This guide covers health endpoints for TypeScript APIs and how to set up uptime monitoring with Vigilmon.

TypeScript Runtime Quick Reference

Tool Use Case Production?
s-node Development, scripts No (slow startup)
sx Development, fast reload No (not optimized)
sc + node Production Yes (compiled JS)
esbuild/swc + node Production Yes (fast build)

For production, compile first. Vigilmon monitors the running server either way.

Express.js Health Endpoint

` ypescript
// src/app.ts
import express, { Request, Response } from "express";

const app = express();
const startTime = Date.now();

interface HealthResponse {
status: "ok" | "error";
uptime: number;
timestamp: string;
version?: string;
}

app.get("/health", (req: Request, res: Response) => {
res.json({
status: "ok",
uptime: Math.floor((Date.now() - startTime) / 1000),
timestamp: new Date().toISOString(),
version: process.env.npm_package_version,
});
});

export default app;
`

` ypescript
// src/server.ts
import app from "./app";

const PORT = parseInt(process.env.PORT || "3000");

const server = app.listen(PORT, () => {
console.log(Server running on port );
});

// Graceful shutdown
process.on("SIGTERM", () => {
server.close(() => {
console.log("Server closed");
process.exit(0);
});
});
`

Fastify + TypeScript Health Check

` ypescript
import Fastify, { FastifyReply, FastifyRequest } from "fastify";

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

interface HealthBody {
status: string;
uptime: number;
}

fastify.get<{ Reply: HealthBody }>("/health", async (
request: FastifyRequest,
reply: FastifyReply
) => {
return reply.send({
status: "ok",
uptime: process.uptime(),
});
});

const start = async () => {
try {
await fastify.listen({ port: 3000, host: "0.0.0.0" });
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};

start();
`

NestJS Health Module

` ypescript
// health/health.controller.ts
import { Controller, Get } from "@nestjs/common";
import {
HealthCheckService,
HttpHealthIndicator,
TypeOrmHealthIndicator,
HealthCheck,
} from "@nestjs/terminus";

@Controller("health")
export class HealthController {
constructor(
private health: HealthCheckService,
private http: HttpHealthIndicator,
private db: TypeOrmHealthIndicator
) {}

@get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck("database"),
]);
}
}
`

`ash

Install NestJS Terminus

npm install @nestjs/terminus
`

Deep Health Check with Multiple Dependencies

` ypescript
// src/health/health.service.ts
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import Redis from "ioredis";

export type HealthStatus = "ok" | "degraded" | "error";

interface ComponentHealth {
status: "ok" | "error";
latencyMs?: number;
error?: string;
}

export interface SystemHealth {
status: HealthStatus;
timestamp: string;
uptime: number;
components: {
database?: ComponentHealth;
cache?: ComponentHealth;
};
}

@Injectable()
export class HealthService {
constructor(private redis: Redis) {}

async check(): Promise {
const [dbResult, cacheResult] = await Promise.allSettled([
this.checkDatabase(),
this.checkRedis(),
]);

const db = dbResult.status === "fulfilled" ? dbResult.value : { status: "error" as const, error: String(dbResult.reason) };
const cache = cacheResult.status === "fulfilled" ? cacheResult.value : { status: "error" as const, error: String(cacheResult.reason) };

const allOk = db.status === "ok" && cache.status === "ok";
const anyError = db.status === "error" || cache.status === "error";

return {
  status: allOk ? "ok" : anyError ? "error" : "degraded",
  timestamp: new Date().toISOString(),
  uptime: process.uptime(),
  components: { database: db, cache },
};
Enter fullscreen mode Exit fullscreen mode

}

private async checkDatabase(): Promise {
const start = Date.now();
try {
// TypeORM ping or your own query
return { status: "ok", latencyMs: Date.now() - start };
} catch (error) {
return { status: "error", error: String(error) };
}
}

private async checkRedis(): Promise {
const start = Date.now();
try {
await this.redis.ping();
return { status: "ok", latencyMs: Date.now() - start };
} catch (error) {
return { status: "error", error: String(error) };
}
}
}
`

Production Build Setup

json
// package.json
{
"scripts": {
"build": "tsc --project tsconfig.build.json",
"start": "node dist/server.js",
"start:dev": "tsx watch src/server.ts",
"start:debug": "ts-node --inspect src/server.ts"
}
}

json
// tsconfig.build.json
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"],
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
}
}

Dockerfile for TypeScript API

`dockerfile

Build stage

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig*.json ./
COPY src/ ./src/
RUN npm run build

Runtime stage

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
EXPOSE 3000
USER node
CMD ["node", "dist/server.js"]
`

Setting Up Vigilmon

  1. Go to vigilmon.online
  2. Click Add Monitor
  3. Configure:

  4. Add SSL monitoring for your domain

  5. Connect Slack for alerts

Type-Safe Monitoring Response

Define a shared type for your health responses:

` ypescript
// types/health.ts
export type HealthStatus = "ok" | "degraded" | "error";

export interface HealthResponse {
status: HealthStatus;
timestamp: string;
uptime: number;
version?: string;
components?: Record status: "ok" | "error";
latencyMs?: number;
}>;
}

// Use in your route
app.get("/health", (req: Request, res: Response) => {
const response: HealthResponse = {
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
};

// Type-safe: TypeScript ensures the shape matches
res.json(response);
});
`

Summary

  • Compile TypeScript for production; use ts-node/tsx for development only
  • Add a typed /health endpoint to every API
  • Return 200 for healthy, 503 for degraded/error
  • Use Promise.allSettled to check all dependencies in parallel
  • Monitor with Vigilmon for external uptime visibility

Start monitoring your TypeScript API at vigilmon.online - free for 3 monitors.

Top comments (0)