Skip to content

Next.js App Router API Routes

Problem Statement

Developers transitioning to Next.js's new app directory often struggle to migrate existing API routes from the pages/api structure. The common error Cannot find module for page: /api/... indicates improper file naming or placement of API handlers in the app directory. This occurs because:

  • The app folder requires different conventions than pages
  • File names are strictly enforced (page.js vs route.js)
  • Route Handlers replace traditional API route syntax

Solution: Route Handlers in App Directory

Since Next.js 13.2 (stable as of 13.4), API endpoints are implemented using Route Handlers. These live in the app directory and use file-based routing:

  1. File Must Be Named route.js|ts
    Exactly - not routes, page, or pages
  2. Export HTTP Method Handlers
    Directly export GET, POST, etc. functions
  3. Folder Structure Mirrors URL Path
    app/api/accounts/login/route.ts/api/accounts/login

Directory Structure

plaintext
my-app/
├── app/
│   ├── api/
│   │   ├── accounts/
│   │   │   ├── login/
│   │   │   │   └── route.ts  # ← API endpoint
│   │   │   └── register/
│   │   │       └── route.ts
│   │   └── users/
│   │       └── route.ts

Implementation Example

Create app/api/accounts/login/route.ts:

ts
import { NextResponse } from 'next/server';

// Handle POST /api/accounts/login
export async function POST(request: Request) {
  const data = await request.json();
  
  // Authentication logic
  if (isValid(data)) {
    return NextResponse.json({ status: 'success' });
  }
  
  return NextResponse.json(
    { error: 'Invalid credentials' },
    { status: 401 }
  );
}

// Optional: Support other HTTP methods
export async function GET() {
  return NextResponse.json({ message: 'Method not allowed' }, { status: 405 });
}

Key Features Explained

HTTP Method Handling

Export explicit methods:

ts
// Separate exports for each supported method
export function GET() { ... }
export function POST() { ... }
export function DELETE() { ... }

Unlike Pages Router

  • No req/res objects - use NextRequest/NextResponse
  • No default export - export named HTTP methods instead

Request/Response Differences

Pages (/pages/api)App Router (/app/api)
req.queryrequest.nextUrl.searchParams
res.status(200)NextResponse.json({}, status: 200)
req.bodyawait request.json()

Common Pitfalls

  1. Incorrect File Name
    → Must be route.js or route.ts
    page.js | handler.js | routes.js

  2. Wrong Placement

    diff
    - app/api/login/page.js // Won't work
    + app/api/login/route.js // Correct
  3. Legacy pages Conflict
    When both directories exist:

    • pages takes precedence over app routes
    • Delete pages/api for full app migration

Version Compatibility

Route Handlers require Next.js ≥13.2. Verify your version:

json
{
  "dependencies": {
    "next": "^13.2.0" // Minimum requirement
  }
}

Additional Use Cases

Dynamic Routes

text
app/api/users/[id]/route.ts
ts
interface Params {
  id: string;
}

export async function GET(request: Request, { params }: { params: Params }) {
  const userId = params.id;
  // Fetch user by ID
}

Headers & Cookies

ts
export function GET(request: NextRequest) {
  // Read headers
  const userAgent = request.headers.get('user-agent');
  
  // Set cookies
  const response = NextResponse.next();
  response.cookies.set('session', 'token');
  return response;
}

When to Use Which Approach

SituationRecommendation
New projects✅ App Directory
Existing projects migrating from Pages⚠️ Gradual migration
Need Node.js req/res access⚠️ Consider pages/api temporarily