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.
Why Monitor Your Next.js App?
Next.js applications run as serverless functions on platforms like Vercel, or as Node.js servers on custom infrastructure. Either way, they can fail in ways that are invisible without external monitoring -- cold start timeouts, database connection limits, and API route errors that only affect specific paths.
- Serverless cold starts -- Functions that haven't been invoked recently can take seconds to start, causing timeout errors for monitoring and users
- Database connection limits -- Each serverless invocation may open a new database connection, quickly exhausting connection pools
- Build/deploy failures -- A broken build on Vercel can leave an old version running, or worse, return build error pages
- API route errors -- Server-side errors in API routes return 500s but don't crash the overall deployment
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