# Next.js API Routes Building Server-Side Functionality

Next.js API Routes enable the creation of standalone server-side functionality within a Next.js application, capable of handling HTTP requests and returning JSON data or other responses. API routes reside in the `pages/api` directory, with each file mapping to a specific API endpoint.

## Basic Example

### pages/api/users.js

```javascript
import type { NextApiRequest, NextApiResponse } from 'next';

// Get user list
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'GET') {
    const users = [
      { id: 1, name: 'User 1' },
      { id: 2, name: 'User 2' },
      { id: 3, name: 'User 3' },
    ];

    res.status(200).json(users);
  } else if (req.method === 'POST') {
    const user = req.body;

    // Simulate database connection
    // await addUserToDatabase(user);

    res.status(201).json({ message: 'User added successfully.' });
  } else {
    res.setHeader('Allow', ['GET', 'POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}
```

1. The `pages/api/users.js` file defines an API route accessible at `/api/users`.
    
2. The `handler` function accepts two parameters: `req` (a `NextApiRequest` object representing the HTTP request) and `res` (a `NextApiResponse` object for the HTTP response).
    
3. For GET requests, it returns a user list (hardcoded here, but typically queried from a database in practice).
    
4. For POST requests, it accepts user data from the request body, simulates adding it to a database, and returns a success message.
    
5. For unsupported methods, it returns a 405 Method Not Allowed error with allowed methods specified.
    

> Next.js API Routes handle JSON responses by default, but you can return other content types as needed. For example, use `res.send` to return HTML.

## Middleware and Request Handling Chain

Next.js API Routes support a middleware pattern, allowing preprocessing of requests or post-processing of responses before reaching the final handler. This is useful for validating headers, authentication, logging, etc.

### Middleware Example

To validate an API key for all API requests:

```javascript
// pages/api/middleware/authenticate.ts
export function authenticate(req: NextApiRequest, res: NextApiResponse, next: () => void) {
  const apiKey = req.headers['x-api-key'];
  
  if (!apiKey || apiKey !== process.env.API_KEY) {
    return res.status(401).json({ message: 'Unauthorized' });
  }
  
  next();
}
```

Apply the middleware in an API route:

```javascript
// pages/api/users.js
import { authenticate } from './middleware/authenticate';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  authenticate(req, res, () => {
    // Original logic
  });
}
```

## Error Handling

Robust error handling is critical for production-grade applications. Next.js API Routes allow custom error handling logic.

### Error Handling Example

```javascript
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    // Code that might throw an error
    const result = await fetchDataFromDatabase();

    res.status(200).json(result);
  } catch (error) {
    console.error('Error occurred:', error);
    res.status(500).json({ error: 'An error occurred while processing your request.' });
  }
}
```

## Type Safety

Using TypeScript for type annotations enhances code robustness and maintainability.

### Type Safety Example

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

type User = {
  id: number;
  name: string;
};

export default async function handler(req: NextApiRequest, res: NextApiResponse<User[] | { message: string }>) {
  // ...
}
```

## Interacting with External Services

Most API routes interact with external services like databases or third-party APIs. Here’s how to use `axios` for HTTP requests.

Install `axios`:

```bash
npm install axios
```

Use in an API route:

```javascript
import axios from 'axios';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    const response = await axios.get('https://api.example.com/data');
    res.status(200).json(response.data);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch data from external service.' });
  }
}
```

## Custom and Dynamic Routes

Next.js API Routes support more than single paths, allowing complex routing structures, including dynamic routes.

### Custom Routes

To organize related API endpoints, use subdirectories. For a blog API, you might structure it as:

```plaintext
pages/
  api/
    blog/
      posts.ts          # Handles /api/blog/posts requests
      post/[id].ts      # Dynamic route, handles /api/blog/post/:id requests
```

### Dynamic Routes

Dynamic routes capture URL segments as parameters. In the above example, `[id]` is a dynamic segment replaced by an actual ID. Access these parameters via `req.query`.

### Dynamic Route Example (pages/api/blog/post/\[id\].ts)

```javascript
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { id } = req.query; // Get dynamic ID

  if (!id) {
    return res.status(400).json({ message: 'Missing post ID' });
  }

  try {
    const post = await getPostById(id as string); // Assume this fetches a post from a database
    if (!post) {
      return res.status(404).json({ message: 'Post not found' });
    }
    return res.status(200).json(post);
  } catch (error) {
    console.error('Error fetching post:', error);
    return res.status(500).json({ message: 'Internal server error' });
  }
}
```

## API Route Caching

To improve performance, you may want to cache API responses. Next.js doesn’t provide built-in API caching, but you can use client-side libraries like `swr` or server-side caching with services like Redis.

### Server-Side Caching Example (Using Redis)

Install `redis` and `ioredis`:

```bash
npm install redis ioredis
```

Use Redis to cache data in an API route:

```javascript
import redis from 'ioredis';

const redisClient = new redis(process.env.REDIS_URL);

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { id } = req.query;

  let post;
  try {
    // Try fetching from Redis cache
    post = await redisClient.get(`post:${id}`);
    if (post) {
      post = JSON.parse(post);
      return res.status(200).json(post);
    }
  } catch (err) {
    console.error('Redis error:', err);
  }

  // Fetch from database if not cached
  post = await getPostById(id as string);

  if (post) {
    // Store in Redis for future requests
    redisClient.set(`post:${id}`, JSON.stringify(post));
    res.status(200).json(post);
  } else {
    res.status(404).json({ message: 'Post not found' });
  }
}
```

## CORS Support

Cross-Origin Resource Sharing (CORS) is a key aspect of web security. Next.js API Routes support CORS by default, but you can further control CORS policies.

### CORS Example

```javascript
import Cors from 'cors'; // Install cors library

// Initialize CORS middleware
const cors = Cors({
  methods: ['GET', 'HEAD'],
});

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  // Apply CORS middleware
  await new Promise((resolve, reject) => {
    cors(req, res, (result) => {
      if (result instanceof Error) {
        reject(result);
      } else {
        resolve(result);
      }
    });
  });

  // Subsequent handling logic
}
```
