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