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 },
};
}
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
- Go to vigilmon.online
- Click Add Monitor
-
Configure:
- URL: https://yourdomain.com/health
- Method: GET
- Expected status: 200
- Interval: 1 minute
Add SSL monitoring for your domain
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)