DEV Community

Vigilmon
Vigilmon

Posted on

How to Monitor Apps Using Drizzle ORM with Vigilmon

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',
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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 };
  }
}
Enter fullscreen mode Exit fullscreen mode

Setting Up Vigilmon

  1. Sign up at vigilmon.online
  2. Add HTTP monitor → your /health endpoint
  3. Configure assertion: expect status field to equal "ok"
  4. Add SSL monitor for your domain
  5. Set alerts: email + Slack on failure

Vigilmon's multi-region checks ensure you catch issues whether they're regional or global.

Monitoring Checklist

  • [ ] /health endpoint 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

Start monitoring your Drizzle app free →

Top comments (0)