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 thanpages
- File names are strictly enforced (
page.js
vsroute.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:
- File Must Be Named
route.js|ts
Exactly - notroutes
,page
, orpages
- Export HTTP Method Handlers
Directly exportGET
,POST
, etc. functions - Folder Structure Mirrors URL Path
app/api/accounts/login/route.ts
→/api/accounts/login
Directory Structure
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
:
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:
// Separate exports for each supported method
export function GET() { ... }
export function POST() { ... }
export function DELETE() { ... }
Unlike Pages Router
- No
req
/res
objects - useNextRequest
/NextResponse
- No default export - export named HTTP methods instead
Request/Response Differences
Pages (/pages/api ) | App Router (/app/api ) |
---|---|
req.query | request.nextUrl.searchParams |
res.status(200) | NextResponse.json({}, status: 200) |
req.body | await request.json() |
Common Pitfalls
Incorrect File Name
→ Must beroute.js
orroute.ts
❌page.js
|handler.js
|routes.js
Wrong Placement
diff- app/api/login/page.js // Won't work + app/api/login/route.js // Correct
Legacy
pages
Conflict
When both directories exist:pages
takes precedence overapp
routes- Delete
pages/api
for fullapp
migration
Version Compatibility
Route Handlers require Next.js ≥13.2. Verify your version:
{
"dependencies": {
"next": "^13.2.0" // Minimum requirement
}
}
Additional Use Cases
Dynamic Routes
app/api/users/[id]/route.ts
interface Params {
id: string;
}
export async function GET(request: Request, { params }: { params: Params }) {
const userId = params.id;
// Fetch user by ID
}
Headers & Cookies
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
Situation | Recommendation |
---|---|
New projects | ✅ App Directory |
Existing projects migrating from Pages | ⚠️ Gradual migration |
Need Node.js req /res access | ⚠️ Consider pages/api temporarily |