How to Monitor Apps Using Drizzle ORM with Vigilmon
Drizzle ORM is the TypeScript-first ORM that's grown rapidly in popularity — lightweight, type-safe, and works with any SQL database. If your production app uses Drizzle, here's how to add proper health checks and monitor it with Vigilmon.
Why Database Connectivity Matters for Monitoring
Your app might be running, but if the database connection pool is exhausted or the database is unreachable, your app is functionally down. Health checks should verify real connectivity — not just that the process is alive.
Health Check with Drizzle ORM
PostgreSQL + Drizzle
// lib/health.ts
import { db } from './db'; // your Drizzle instance
import { sql } from 'drizzle-orm';
export async function checkDatabase(): Promise<{
status: 'ok' | 'error';
latencyMs: number;
error?: string;
}> {
const start = Date.now();
try {
await db.execute(sql`SELECT 1`);
return { status: 'ok', latencyMs: Date.now() - start };
} catch (err) {
return {
status: 'error',
latencyMs: Date.now() - start,
error: err instanceof Error ? err.message : 'Unknown error',
};
}
}
Next.js Health Route with Drizzle
// app/api/health/route.ts (Next.js App Router)
import { NextResponse } from 'next/server';
import { checkDatabase } from '@/lib/health';
export async function GET() {
const dbCheck = await checkDatabase();
const isHealthy = dbCheck.status === 'ok';
return NextResponse.json(
{
status: isHealthy ? 'ok' : 'degraded',
database: dbCheck,
timestamp: new Date().toISOString(),
},
{ status: isHealthy ? 200 : 503 }
);
}
export const runtime = 'nodejs'; // or 'edge'
export const dynamic = 'force-dynamic';
Hono + Drizzle Health Endpoint
import { Hono } from 'hono';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { sql } from 'drizzle-orm';
const client = postgres(process.env.DATABASE_URL!);
const db = drizzle(client);
const app = new Hono();
app.get('/health', async (c) => {
const start = Date.now();
try {
await db.execute(sql`SELECT 1`);
return c.json({
status: 'ok',
database: {
status: 'connected',
latencyMs: Date.now() - start,
},
uptime: process.uptime(),
timestamp: new Date().toISOString(),
});
} catch (err) {
return c.json(
{
status: 'error',
database: {
status: 'disconnected',
error: err instanceof Error ? err.message : 'unknown',
latencyMs: Date.now() - start,
},
},
503
);
}
});
export default app;
Express + Drizzle Health Endpoint
import express from 'express';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { sql } from 'drizzle-orm';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool);
const app = express();
app.get('/health', async (req, res) => {
const start = Date.now();
try {
await db.execute(sql`SELECT 1`);
res.json({
status: 'ok',
database: 'connected',
latencyMs: Date.now() - start,
uptime: process.uptime(),
});
} catch (err) {
res.status(503).json({
status: 'error',
database: 'disconnected',
error: err instanceof Error ? err.message : 'unknown',
});
}
});
app.listen(3000);
With Multiple Databases (Drizzle Multi-DB)
import { db as primaryDb } from './db/primary';
import { db as replicaDb } from './db/replica';
import { sql } from 'drizzle-orm';
async function fullHealthCheck() {
const checks: Record<string, { status: string; latencyMs: number }> = {};
// Check primary
const primaryStart = Date.now();
try {
await primaryDb.execute(sql`SELECT 1`);
checks.primary = { status: 'ok', latencyMs: Date.now() - primaryStart };
} catch {
checks.primary = { status: 'error', latencyMs: Date.now() - primaryStart };
}
// Check replica
const replicaStart = Date.now();
try {
await replicaDb.execute(sql`SELECT 1`);
checks.replica = { status: 'ok', latencyMs: Date.now() - replicaStart };
} catch {
checks.replica = { status: 'error', latencyMs: Date.now() - replicaStart };
}
return checks;
}
Drizzle Migration Health
Also verify your migrations are up to date:
import { drizzle } from 'drizzle-orm/node-postgres';
import { migrate } from 'drizzle-orm/node-postgres/migrator';
async function checkMigrationStatus() {
try {
// This will throw if migrations are pending (depending on your strategy)
await migrate(db, { migrationsFolder: './drizzle' });
return { migrations: 'up-to-date' };
} catch (err) {
return { migrations: 'error', error: err.message };
}
}
Setting Up Vigilmon
- Sign up at vigilmon.online
-
Add HTTP monitor → your
/healthendpoint -
Configure assertion: expect
statusfield to equal"ok" - Add SSL monitor for your domain
- Set alerts: email + Slack on failure
Vigilmon's multi-region checks ensure you catch issues whether they're regional or global.
Monitoring Checklist
- [ ]
/healthendpoint checks actual DB connectivity (not just process alive) - [ ] Returns HTTP 503 when DB is unreachable (not 200)
- [ ] Latency measured and logged
- [ ] Multiple DB checks if using primary + replica
- [ ] Vigilmon uptime monitor active
- [ ] SSL certificate monitor active
- [ ] Alert channels configured
Top comments (0)