Monitoring TypeScript Applications: From Metrics to Smart Alerts
In modern development, writing code is only half the battle. It is critically important to understand how an application behaves in production. For TypeScript projects, whether it's a server on Node.js or a client on React, proactive monitoring becomes a mandatory requirement. In this article, we will break down advanced techniques for collecting metrics, setting up alerts, and building an observability system that will let you sleep soundly.
1. Key Metrics for TypeScript Applications
Before setting up alerts, you need to determine what exactly we are measuring. Let's divide the metrics into three groups: infrastructure, business, and user.
Infrastructure Metrics (RED Methodology):
- Rate — number of requests per second (RPS).
- Errors — percentage of errors (5xx, 4xx, unhandled rejections).
- Duration — response time (p50, p95, p99).
Business Metrics:
- Number of successful transactions.
- Execution time of critical operations (e.g., report generation).
- Active users (DAU/MAU).
User Metrics (for client applications):
- LCP (Largest Contentful Paint) — main content load speed.
- FID (First Input Delay) — first interaction delay.
- CLS (Cumulative Layout Shift) — visual layout stability.
To collect these metrics in TypeScript, we will use the prom-client library (for Node.js) and @opentelemetry/instrumentation-http.
2. Collecting Metrics with Prometheus and OpenTelemetry
Let's look at an example of setting up metrics for an Express application in TypeScript. Install the necessary packages:
npm install prom-client @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/instrumentation-http @opentelemetry/instrumentation-expressCreate a metrics.ts file to register metrics:
import client from 'prom-client';
const collectDefaultMetrics = client.collectDefaultMetrics;collectDefaultMetrics({ timeout: 5000 });
export const httpRequestDuration = new client.Histogram({ name: 'http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'status_code'], buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],});
export const errorCounter = new client.Counter({ name: 'http_errors_total', help: 'Total number of HTTP errors', labelNames: ['method', 'route', 'status_code'],});Now integrate the metrics into middleware:
import { Request, Response, NextFunction } from 'express';
export function metricsMiddleware(req: Request, res: Response, next: NextFunction) { const end = httpRequestDuration.startTimer(); res.on('finish', () => { end({ method: req.method, route: req.route?.path || 'unknown', status_code: res.statusCode.toString() }); if (res.statusCode >= 500) { errorCounter.inc({ method: req.method, route: req.route?.path || 'unknown', status_code: res.statusCode.toString() }); } }); next();}Add an endpoint for Prometheus metric collection:
import { Router } from 'express';import client from 'prom-client';
const router = Router();router.get('/metrics', async (req, res) => { res.set('Content-Type', client.register.contentType); res.end(await client.register.metrics());});export default router;For client applications (React), use @opentelemetry/instrumentation-document-load for automatic Web Vitals collection.
3. Setting Up Advanced Alerts
Alerts should be smart so as not to generate