Framework Guide

Next.js Monitoring

Set up health check endpoints and uptime monitoring for your Next.js application. Works with both Pages Router and App Router.

App Router (Next.js 13+)

// app/api/health/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  return NextResponse.json({
    status: 'healthy',
    timestamp: new Date().toISOString()
  });
}

// With dependency checks
// app/api/ready/route.ts
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';

export async function GET() {
  const checks: Record = {};
  let healthy = true;

  // Check database
  try {
    const start = Date.now();
    await db.execute('SELECT 1');
    checks.database = {
      status: 'ok',
      responseTimeMs: Date.now() - start
    };
  } catch (error) {
    healthy = false;
    checks.database = {
      status: 'error',
      message: error instanceof Error ? error.message : 'Unknown error'
    };
  }

  return NextResponse.json({
    status: healthy ? 'healthy' : 'unhealthy',
    timestamp: new Date().toISOString(),
    checks
  }, { status: healthy ? 200 : 503 });
}

Pages Router

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

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.status(200).json({
    status: 'healthy',
    timestamp: new Date().toISOString()
  });
}

// pages/api/ready.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { prisma } from '@/lib/prisma';

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const checks: Record = {};
  let healthy = true;

  try {
    const start = Date.now();
    await prisma.$queryRaw`SELECT 1`;
    checks.database = {
      status: 'ok',
      responseTimeMs: Date.now() - start
    };
  } catch (error) {
    healthy = false;
    checks.database = { status: 'error' };
  }

  res.status(healthy ? 200 : 503).json({
    status: healthy ? 'healthy' : 'unhealthy',
    checks
  });
}

Edge Runtime Health Check

// app/api/health/route.ts
import { NextResponse } from 'next/server';

export const runtime = 'edge';

export async function GET() {
  return NextResponse.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    runtime: 'edge'
  });
}

Check External APIs

// app/api/ready/route.ts
async function checkExternalAPI(url: string, name: string) {
  try {
    const start = Date.now();
    const response = await fetch(url, {
      method: 'HEAD',
      signal: AbortSignal.timeout(5000)
    });
    return {
      status: response.ok ? 'ok' : 'error',
      responseTimeMs: Date.now() - start,
      statusCode: response.status
    };
  } catch (error) {
    return {
      status: 'error',
      message: error instanceof Error ? error.message : 'Unknown error'
    };
  }
}

export async function GET() {
  const checks = await Promise.all([
    checkExternalAPI('https://api.stripe.com/v1', 'stripe'),
    checkExternalAPI('https://api.github.com', 'github')
  ]);

  return NextResponse.json({
    status: 'healthy',
    checks: {
      stripe: checks[0],
      github: checks[1]
    }
  });
}

Vercel Deployment Considerations

  • Cold starts — Serverless functions may be slow on first request
  • Function timeouts — Default 10s (hobby) or 60s (pro)
  • Edge vs Serverless — Edge functions are faster but have limitations
  • No persistent connections — Database connections are per-request

Best Practices

  • Use Edge runtime for simple checks — Faster response times globally
  • Add timeouts to external checks — AbortSignal.timeout() prevents hangs
  • Monitor serverless function invocations — Track cold start frequency
  • Include version info — Helps debug deployment issues

Monitor your Next.js app

Add your health endpoint to UptimeSignal and get alerted when it fails.

Start monitoring free →

More Framework Guides