Next.js API Routes App Router 7 min read

Next.js API Route 500 Error? How to Fix Route Handler Errors

Diagnose Next.js API route failures — 500 with no browser message, edge runtime Node.js errors, body parsing broken in App Router, CORS rejections, and Pages vs App Router export differences.

1. 500 Error With No Message — Check the Terminal

Next.js intentionally returns a blank 500 Internal Server Error body in production so your stack traces never leak to the browser. The actual error — with the message, file path, and line number — is always printed to the terminal running the Next.js process.

	Error: Cannot read properties of undefined (reading 'id')
    at GET (app/api/user/route.ts:12:22)
    at async NextRequest ...
  • Dev server: the terminal running next dev prints the full error on every failed request — scroll up after the 500 appears in the browser.
  • Production (Vercel): go to Vercel dashboard → your project → Deployments → Functions tab → select the route to see per-request logs.
  • Wrap handlers in try/catch: catch the error and return a 500 response with the message during development to surface it in the browser network tab.
// app/api/example/route.ts
export async function GET(request: Request) {
  try {
    const data = await fetchSomething();
    return Response.json(data);
  } catch (err) {
    console.error(err);           // always check terminal
    return Response.json(
      { error: (err as Error).message },
      { status: 500 }
    );
  }
}

2. "The Edge Runtime Does Not Support Node.js..." Error

This error fires when your Route Handler (or a package it imports) uses Node.js-only APIs — fs, http, Buffer, or packages like axios that depend on them — while the route is running in the Edge runtime.

Fix option A — switch to Node.js runtime: add one export to the top of your route file.
// app/api/heavy/route.ts
export const runtime = 'nodejs';   // ← add this line

export async function GET() {
  // fs, axios, Buffer, etc. all work here now
  return Response.json({ ok: true });
}
Fix option B — replace with Web API equivalents: use global fetch() instead of axios, use the Web Crypto API instead of Node crypto.
// Before (breaks on edge)
import axios from 'axios';
const res = await axios.get('https://api.example.com/data');

// After (edge-compatible)
const res = await fetch('https://api.example.com/data');
const data = await res.json();

3. Body Parsing: req.body is Undefined in App Router

The Pages Router (pages/api/) automatically parses the body into req.body. App Router Route Handlers (app/api/) receive a standard Web API Request — there is no req.body property. You must read the body explicitly:

// app/api/submit/route.ts
export async function POST(request: Request) {
  // JSON body
  const body = await request.json();

  // Form data (multipart or application/x-www-form-urlencoded)
  const form = await request.formData();
  const name = form.get('name');

  // Raw text
  const text = await request.text();

  return Response.json({ received: true });
}
  • body-parser does not apply: middleware that works with the Pages Router (like body-parser) is not compatible with App Router Route Handlers.
  • Content-Type must match: request.json() throws if the request body is not valid JSON — make sure the client sets Content-Type: application/json.
  • Read body only once: the Request body is a stream — calling request.json() a second time throws body already consumed. Read it once and store the result in a variable.

4. CORS Errors From API Routes

Browsers block cross-origin requests to your API unless the response includes the correct CORS headers. In App Router, return the headers on every response and export an OPTIONS handler to answer preflight requests:

// app/api/data/route.ts
const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};

export function OPTIONS() {
  return new Response(null, { status: 204, headers: corsHeaders });
}

export async function GET() {
  return Response.json({ data: 'hello' }, { headers: corsHeaders });
}

To apply CORS globally across all routes without touching individual files, use next.config.js:

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/api/:path*',
        headers: [
          { key: 'Access-Control-Allow-Origin', value: '*' },
          { key: 'Access-Control-Allow-Methods', value: 'GET,POST,OPTIONS' },
        ],
      },
    ];
  },
};

5. Pages Router vs App Router — File Names and Exports

The two routers have different file names and export conventions. Mixing them up causes routes to silently 404 or be ignored.

// Pages Router — pages/api/hello.ts
// Default export, req/res pattern
export default function handler(req, res) {
  res.json({ message: 'hello' });
}

// App Router — app/api/hello/route.ts
// Named exports per HTTP method, no default export
export async function GET(request: Request) {
  return Response.json({ message: 'hello' });
}

export async function POST(request: Request) {
  const body = await request.json();
  return Response.json({ received: body });
}
  • File name: Pages Router uses any file name under pages/api/; App Router requires the file to be named route.ts (or route.js) inside a directory under app/.
  • Named exports only: App Router ignores a default export — exporting export default function handler from a route.ts file will result in a 405 Method Not Allowed.
  • Middleware scope: Next.js middleware (middleware.ts) matches all routes by default. Set the matcher config to restrict it and avoid it running on /api/ unintentionally.
🔔

Know when Vercel has an outage affecting your Next.js app

Free email alerts when Vercel or Next.js infrastructure has incidents — no credit card needed.

FAQ

Why does my Next.js API route return 500 with no error message in the browser?

Next.js hides server-side errors from the browser in production. Open the terminal running next dev or check your hosting provider's function logs — the full stack trace is always printed there.

What does "The edge runtime does not support Node.js" mean?

Your Route Handler is set to run on the Edge runtime but uses a Node.js-only package. Export export const runtime = 'nodejs' from the route file, or replace Node.js-only imports with Web API equivalents like global fetch() instead of axios.

Why is req.body undefined in App Router Route Handlers?

App Router Route Handlers receive a standard Web API Request object, not the Express-style req from Pages Router. Use await request.json() to read a JSON body.

How do I add CORS headers to a Next.js API route?

In App Router, include Access-Control-Allow-Origin in your Response headers and export an OPTIONS function for preflight. For global CORS, use the headers() function in next.config.js.

Monitor related services