DEV Community

Vigilmon
Vigilmon

Posted on

How to Monitor Your tRPC API with Vigilmon

How to Monitor Your tRPC API with Vigilmon

tRPC gives you end-to-end type safety between your TypeScript backend and frontend-but it doesn't give you uptime monitoring. Your tRPC server needs external health checks the same as any other API. This guide shows how to add a health endpoint to a tRPC server and monitor it with Vigilmon.

tRPC Architecture Quick Overview

tRPC APIs typically run on:

  • Express or Fastify adapter
  • Next.js API routes
  • Standalone HTTP server

Each deployment target has a slightly different health endpoint approach.

Health Endpoint with Express Adapter

` ypescript
// server.ts
import express from "express";
import * as trpcExpress from "@trpc/server/adapters/express";
import { appRouter } from "./router";
import { createContext } from "./context";

const app = express();

// Health endpoint BEFORE tRPC middleware
app.get("/health", (req, res) => {
res.json({
status: "ok",
uptime: process.uptime(),
timestamp: new Date().toISOString(),
});
});

app.use(
"/trpc",
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
})
);

app.listen(3000, () => {
console.log("Server running on port 3000");
});
`

The health endpoint at /health lives outside tRPC routing, so it works even if tRPC fails to initialize.

Health Endpoint with Fastify Adapter

` ypescript
// server.ts
import Fastify from "fastify";
import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify";
import { appRouter } from "./router";
import { createContext } from "./context";

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

// Health check
server.get("/health", async () => ({
status: "ok",
uptime: process.uptime(),
timestamp: new Date().toISOString(),
}));

server.register(fastifyTRPCPlugin, {
prefix: "/trpc",
trpcOptions: { router: appRouter, createContext },
});

const start = async () => {
await server.listen({ port: 3000, host: "0.0.0.0" });
};

start();
`

Health Route in Next.js (App Router)

` ypescript
// app/api/health/route.ts
import { NextResponse } from "next/server";

export async function GET() {
return NextResponse.json({
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
});
}
`

Vigilmon monitors https://yourdomain.com/api/health - outside tRPC's /api/trpc namespace.

Health Route in Next.js (Pages Router)

` ypescript
// pages/api/health.ts
import type { NextApiRequest, NextApiResponse } from "next";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "GET") {
return res.status(405).json({ error: "Method not allowed" });
}

res.status(200).json({
status: "ok",
timestamp: new Date().toISOString(),
});
}
`

Deep Health Check with Database

` ypescript
// server/health.ts
import { db } from "./db"; // Your Prisma/Drizzle/etc. client

export interface HealthResult {
status: "ok" | "error";
timestamp: string;
uptime: number;
components: {
database: { status: "ok" | "error"; latencyMs?: number; error?: string };
};
}

export async function getHealth(): Promise<[HealthResult, number]> {
const start = Date.now();

try {
await db.SELECT 1;
const dbLatency = Date.now() - start;

return [
  {
    status: "ok",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    components: { database: { status: "ok", latencyMs: dbLatency } },
  },
  200,
];
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
return [
{
status: "error",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
components: {
database: {
status: "error",
error: error instanceof Error ? error.message : "Unknown",
},
},
},
503,
];
}
}

// Use in Express:
app.get("/health", async (req, res) => {
const [result, statusCode] = await getHealth();
res.status(statusCode).json(result);
});
`

tRPC Health Procedure (Optional)

Some teams add a health procedure inside tRPC for client-side checks:

` ypescript
// router/health.ts
import { publicProcedure, router } from "../trpc";

export const healthRouter = router({
ping: publicProcedure.query(() => ({
status: "ok" as const,
timestamp: new Date().toISOString(),
})),
});

// app/router.ts
import { router } from "../trpc";
import { healthRouter } from "./health";
import { userRouter } from "./user";

export const appRouter = router({
health: healthRouter,
user: userRouter,
});
`

This lets your frontend check health via rpc.health.ping.query(), but don't use this for Vigilmon-Vigilmon needs a plain HTTP endpoint, not a tRPC procedure call.

Setting Up Vigilmon

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

  4. Add SSL monitoring for your domain

Validate Your Health Endpoint First

`ash

Test locally

curl http://localhost:3000/health

Test in production

curl https://yourdomain.com/health
`

Expected response:
json
{
"status": "ok",
"uptime": 1234,
"timestamp": "2026-08-03T12:00:00.000Z"
}

Type Safety for Health Responses

Keep your health response type-safe:

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

export interface HealthResponse {
status: HealthStatus;
timestamp: string;
uptime?: number;
components?: Record;
}
`

Use this type in both your Express handler and your Next.js route to keep them consistent.

Summary

  • Add a /health endpoint outside tRPC's namespace
  • Use Express/Fastify route or Next.js API route-not a tRPC procedure
  • Include database connectivity check for complete health visibility
  • Return 200 for healthy, 503 for degraded
  • Monitor externally with Vigilmon

Set up uptime monitoring for your tRPC API at vigilmon.online - free for 3 monitors.

Top comments (0)