# How to Build an API in Next.js 14: Complete Guide to Pages Router Routes

> Learn to build API routes in Next.js 14 using the Pages Router. Export default async functions from pages api files for robust API handling and automatic tracing.

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

---

**The recommended way to handle API routes in a Next.js 14 project is to export a default async function from files inside the `pages/api/` directory, which Next.js automatically wraps with OpenTelemetry tracing and executes through the internal `apiResolver` utility.**

An **api nextjs** project leverages the Pages Router convention where every file under `pages/api/` becomes a serverless endpoint. According to the [`vercel/next.js`](https://github.com/vercel/next.js/blob/main/vercel/next.js) source code, the framework handles module validation, request/response augmentation, and error handling automatically through a dedicated pipeline in [`packages/next/src/server/route-modules/pages-api/module.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/route-modules/pages-api/module.ts).

## How API Routes Work in Next.js 14

### File-Based Routing Convention

Next.js treats every file under `pages/api/` as a separate serverless function. When a request hits `/api/hello`, the framework:

1. **Maps the URL to the file** ([`pages/api/hello.ts`](https://github.com/vercel/next.js/blob/main/pages/api/hello.ts))
2. **Loads the module** and validates that it exports a default function (validation logic in [`packages/next/src/server/route-modules/pages-api/module.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/route-modules/pages-api/module.ts) lines 18-23)
3. **Wraps the handler** with `wrapApiHandler` to attach OpenTelemetry tracing spans ([`packages/next/src/server/api-utils/index.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/api-utils/index.ts) lines 23-38)
4. **Executes through `apiResolver`** which supplies body parsing, cookie handling, and response helpers ([`packages/next/src/server/api-utils/node/api-resolver.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts) lines 31-45)

### Core Architecture Components

| Component | Purpose | Source Location |
|-----------|---------|-----------------|
| **Page-API Module** | Validates default export and executes handler via `apiResolver` | [`packages/next/src/server/route-modules/pages-api/module.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/route-modules/pages-api/module.ts) |
| **`wrapApiHandler`** | Adds OpenTelemetry tracing (`next.route`) around requests | [`packages/next/src/server/api-utils/index.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/api-utils/index.ts) |
| **`apiResolver`** | Augments request/response objects, handles body parsing, enforces response limits | [`packages/next/src/server/api-utils/node/api-resolver.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts) |

## Recommended Practices for API Next.js Routes

Export a single async function as the default from your API files. The function receives typed `NextApiRequest` and `NextApiResponse` objects that include framework-provided helpers.

Rely on the **built-in body parser** enabled by default. Disable it only when handling raw streams:

```typescript
export const config = {
  api: { bodyParser: false }
}

```

Use the **response helpers** (`res.status`, [`res.json`](https://github.com/vercel/next.js/blob/main/res.json), `res.redirect`, `res.revalidate`) attached by `apiResolver`. These guarantee proper header handling and respect the default 2 MB response-size limit (configurable via `api.responseLimit`).

Throw **`ApiError`** for intentional HTTP error responses. The resolver catches these and sends appropriate status codes without exposing stack traces in production.

Never return a value from the handler. Next.js expects the handler to end the response itself. Returning a value triggers a warning (see the warning logic at line 38 of `apiResolver`).

## Code Examples

### Basic Hello World Endpoint

```typescript
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default async (_req: NextApiRequest, res: NextApiResponse) => {
  // res.json is provided by the framework
  res.json({ message: 'Hello, world!' })
}

```

This matches the minimal example used in the repository's test suite at [`test/production/graceful-shutdown/src/pages/api/fast.ts`](https://github.com/vercel/next.js/blob/main/test/production/graceful-shutdown/src/pages/api/fast.ts).

### POST Handler with Validation

```typescript
// pages/api/users.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import { ApiError } from 'next/dist/server/api-utils'

export const config = {
  api: { bodyParser: { sizeLimit: '500kb' } }
}

export default async (req: NextApiRequest, res: NextApiResponse) => {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method Not Allowed' })
  }

  const { name, email } = req.body ?? {}
  if (!name || !email) {
    throw new ApiError(400, 'Missing name or email')
  }

  const user = { id: Date.now(), name, email }
  res.status(201).json(user)
}

```

The `apiResolver` attaches `req.body` via internal `parseBody` and provides `res.status`/[`res.json`](https://github.com/vercel/next.js/blob/main/res.json) helpers (lines 78-85 of [`api-resolver.ts`](https://github.com/vercel/next.js/blob/main/api-resolver.ts)).

### On-Demand Revalidation and Preview Mode

```typescript
// pages/api/revalidate.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default async (req: NextApiRequest, res: NextApiResponse) => {
  // Enable preview mode with secure cookies
  res.setPreviewData({ foo: 'bar' }, { maxAge: 60 * 60 })

  // Revalidate a static page
  await res.revalidate('/posts/123')

  res.json({ revalidated: true })
}

```

`res.setPreviewData`, `res.revalidate`, and `res.setDraftMode` are attached in `apiResolver` (lines 15-21).

### Redirects from API Routes

```typescript
// pages/api/redirect.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default async (_req: NextApiRequest, res: NextApiResponse) => {
  // Permanent redirect (301) to external URL
  res.redirect(301, 'https://example.com')
}

```

The `redirect` helper validates arguments and writes the proper `Location` header (see `api-utils` lines 59-73).

## Summary

- **File-based routing**: Place files under `pages/api/` to create serverless endpoints automatically.
- **Default export**: Export a single async function receiving `NextApiRequest` and `NextApiResponse`.
- **Built-in helpers**: Leverage `res.status`, [`res.json`](https://github.com/vercel/next.js/blob/main/res.json), `res.redirect`, and `res.revalidate` attached by the `apiResolver` in [`packages/next/src/server/api-utils/node/api-resolver.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts).
- **Error handling**: Throw `ApiError` for controlled HTTP errors; the framework hides stack traces in production.
- **Configuration**: Use `export const config` to customize body parsing and response limits.

## Frequently Asked Questions

### How do I disable the default body parser in a Next.js API route?

Add a `config` export with `bodyParser: false`. This is required when handling raw request streams or implementing custom parsers like GraphQL or tRPC middleware.

```typescript
export const config = {
  api: { bodyParser: false }
}

```

### What is the maximum response size for API routes in Next.js?

The default limit is **2 MB**. You can configure this via the `responseLimit` option in your route config, or set it to `false` to disable the check entirely. The enforcement logic resides in [`packages/next/src/server/api-utils/node/api-resolver.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts).

### Can I return a value from my API route handler?

No. Next.js expects the handler to terminate the response using `res.end()`, `res.json()`, or similar methods. Returning a value from the async function triggers a runtime warning and can lead to unexpected behavior, as noted in the resolver logic at line 38 of [`api-resolver.ts`](https://github.com/vercel/next.js/blob/main/api-resolver.ts).

### How does Next.js handle errors thrown in API routes?

The `apiResolver` catches errors automatically. If you throw an `ApiError` (imported from `next/dist/server/api-utils`), it sends the specified status code and message without leaking stack traces in production. Unhandled errors return a 500 status code with generic messaging to prevent information disclosure.