# How to Implement Next.js API Routes in the App Directory

> Learn to implement Next.js API routes in the App Router. Create route.ts files exporting HTTP method handlers for robust backend functionality.

- Repository: [Vercel/next.js](https://github.com/vercel/next.js)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Next.js API routes in the App Router are implemented by creating [`route.ts`](https://github.com/vercel/next.js/blob/main/route.ts) (or `.js`) files inside `app/api/` directories that export uppercase HTTP method handlers like `GET`, `POST`, or `PUT`, which receive a `NextRequest` object and must return a standard `Response` or `NextResponse`.**

Next.js 13 introduced the App Router, fundamentally changing how developers implement nextjs api routes within the `app/` folder structure. Unlike the legacy `pages/api` pattern, the App Router uses a file-system based approach with specific naming conventions and handler signatures. This guide examines the implementation details from the vercel/next.js repository to show you exactly how App Router API routes work under the hood.

## File Structure and Naming Conventions

Unlike the `pages/api` directory where any file became an endpoint, nextjs api routes in the App Router require a specific convention. You create a directory structure inside `app/api/` and place a file named [`route.ts`](https://github.com/vercel/next.js/blob/main/route.ts), [`route.js`](https://github.com/vercel/next.js/blob/main/route.js), or [`route.tsx`](https://github.com/vercel/next.js/blob/main/route.tsx) at the leaf level.

For example:
- [`app/api/hello/route.ts`](https://github.com/vercel/next.js/blob/main/app/api/hello/route.ts) → `/api/hello`
- `app/api/users/[id]/route.ts` → `/api/users/123`

## Exporting HTTP Method Handlers

Each route file exports functions named after HTTP methods in uppercase: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `OPTIONS`, and `HEAD`. These functions receive a `NextRequest` object and should return a `Response` or `NextResponse`.

According to the source code in [`packages/next/src/server/route-modules/app-route/helpers/auto-implement-methods.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/route-modules/app-route/helpers/auto-implement-methods.ts), the framework validates that only uppercase method names are exported and auto-implements `HEAD` by delegating to `GET` if not explicitly defined.

### Simple GET Route Example

```typescript
import { NextResponse } from 'next/server'

export async function GET() {
  return NextResponse.json({ message: 'Hello from the App Router!' })
}

```

### Handling Multiple Methods

```typescript
import { NextResponse } from 'next/server'

// GET – list users
export async function GET() {
  const users = await fetch('https://api.example.com/users').then(r => r.json())
  return NextResponse.json(users)
}

// POST – create a new user
export async function POST(req: Request) {
  const data = await req.json()
  const created = await fetch('https://api.example.com/users', {
    method: 'POST',
    body: JSON.stringify(data),
    headers: { 'Content-Type': 'application/json' },
  }).then(r => r.json())

  return NextResponse.json(created, { status: 201 })
}

```

## Core Architecture of Next.js API Routes

The implementation of nextjs api routes in the App Router relies on several sophisticated systems working together to provide static generation, caching, and edge runtime support.

### Route Module Initialization

When the server receives a request, the `AppRouteRouteModule` class (defined in [`packages/next/src/server/route-modules/app-route/module.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts)) initializes by reading the `dynamic` export from your route file. The constructor (lines 40-78) processes these flags:

- `export const dynamic = 'force-dynamic'` sets `forceDynamic: true`, ensuring the route runs on every request
- `export const dynamic = 'force-static'` sets `forceStatic: true`, allowing static generation at build time
- `export const dynamic = 'error'` sets `dynamicShouldError: true`, throwing an error if dynamic APIs are accessed during static generation

### Request Store and Async Storage

The module creates a request store via `createRequestStoreForAPI` that wraps the standard Web API `Request` into a `NextRequest` object. This process, handled within the route module's execution logic, adds Next.js-specific helpers like `nextUrl`, cookies, and search parameters.

Your handler executes inside several async storage contexts (`workUnitAsyncStorage`, `workAsyncStorage`, `actionAsyncStorage`) that provide static-generation detection, caching support, and server-action capabilities. This architecture allows nextjs api routes to participate in the same rendering pipeline as React Server Components.

### Response Normalization

After your handler finishes executing, the `AppRouteRouteModule` normalizes the response (lines 82-89 of [`module.ts`](https://github.com/vercel/next.js/blob/main/module.ts)) and merges any mutable cookies using `appendMutableCookies`. If you return a standard Web API `Response` or use `NextResponse.json()`, the framework handles header normalization and status code validation.

If you attempt to use forbidden APIs like `NextResponse.rewrite()` inside an API route handler, the module throws a clear error (lines 35-41 of [`module.ts`](https://github.com/vercel/next.js/blob/main/module.ts)) with documentation links, as rewrite operations are reserved for middleware and page routes.

## Dynamic Rendering and Caching Controls

Control whether your nextjs api routes run at build time or request time using the `dynamic` export. Combine this with `revalidate` for fine-grained cache control.

```typescript
import { NextResponse } from 'next/server'

export const dynamic = 'force-dynamic'  // Always render on each request
export const revalidate = 0             // Disable ISR cache

export async function GET() {
  // This runs on every request because of `dynamic = 'force-dynamic'`
  const now = new Date().toISOString()
  return NextResponse.json({ now })
}

```

The `dynamic` export is processed by the `AppRouteRouteModule` constructor to set flags in the `WorkStore` that determine static generation behavior.

## Accessing Request Context and Helpers

Handlers receive a `NextRequest` object (defined in [`packages/next/src/server/web/spec-extension/request.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/web/spec-extension/request.ts)) that extends the standard Web API `Request` with Next.js-specific properties.

```typescript
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
  // `request` is a `NextRequest` with `nextUrl`, `cookies`, etc.
  const locale = request.nextUrl.searchParams.get('lang') ?? 'en'
  const session = request.cookies.get('session')?.value ?? null

  return NextResponse.json({ locale, authenticated: !!session })
}

```

You can also return standard Web API `Response` objects for raw text or custom headers:

```typescript
export async function GET() {
  const body = 'Plain text response'
  return new Response(body, {
    status: 200,
    headers: { 'Content-Type': 'text/plain' },
  })
}

```

## Summary

- Next.js API routes in the App Router use [`route.ts`](https://github.com/vercel/next.js/blob/main/route.ts) files inside `app/api/` directories with uppercase HTTP method exports (`GET`, `POST`, etc.)
- The `AppRouteRouteModule` class in [`packages/next/src/server/route-modules/app-route/module.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) orchestrates request handling, validation, and response normalization
- Use `export const dynamic` to control rendering behavior: `'force-dynamic'` for SSR, `'force-static'` for build-time generation, or `'error'` to fail on dynamic APIs
- Handlers receive a `NextRequest` object with helpers like `nextUrl` and cookies, and should return `Response` or `NextResponse`
- The framework auto-implements `HEAD` from `GET` and manages async storage contexts (`workUnitAsyncStorage`, `workAsyncStorage`, `actionAsyncStorage`) for caching and server actions

## Frequently Asked Questions

### Can I use the pages/api directory alongside app/api routes?

Yes, Next.js supports incremental adoption. You can have both `pages/api` and `app/api` routes in the same project, though routes in `app/api` follow the new Route Handler convention with uppercase method exports while `pages/api` uses default exports. Note that `app/api` routes take precedence over `pages/api` routes when URLs collide.

### Why do I get an error when using NextResponse.rewrite() in an API route?

The `NextResponse.rewrite()` API is designed for middleware and page routes, not API Route Handlers. According to the source code in [`packages/next/src/server/route-modules/app-route/module.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) (lines 35-41), using rewrite inside an app router API handler throws a clear error because the route module expects a direct response, not a rewrite instruction.

### How do I handle dynamic route parameters in app/api routes?

Dynamic segments work identically to page routes. Create a directory with brackets like `app/api/users/[id]/route.ts`. The handler receives a `params` object as the second argument: `export async function GET(request: Request, { params }: { params: { id: string } }) { ... }`. The `AppRouteRouteModule` processes these parameters through [`parsed-url-query-to-params.ts`](https://github.com/vercel/next.js/blob/main/parsed-url-query-to-params.ts) helpers.

### What is the difference between export const dynamic and export const revalidate?

The `dynamic` export controls whether the route is rendered statically at build time or dynamically at request time (`'force-static'`, `'force-dynamic'`, or `'error'`). The `revalidate` export (or `revalidate` option in fetch) controls how long statically generated responses remain valid in the cache when using Incremental Static Regeneration (ISR). Use `dynamic` for rendering behavior, `revalidate` for cache duration on static responses.